Roadmap for the 0.3 series in Valum
The 0.2 series has focused on bringing the basics that will guide the upcoming features.
Here’s the roadmap for the next release 0.3
of Valum:
- aggressive optimization based on
GLib.Sequence
- content negociation
- HTTP authentification (basic and digest)
- static resource delivery
- VSGI loader
- multipart streams
- typed rule parameters using GType
- filters
- Python and Gjs bindings
The following features have been integrated in the trunk:
- flags for HTTP methods
- default
HEAD
toGET
(still need to strip the body) - routing context rather than a stack that provide states and services
- shared libraries for VSGI implementations compatible with
GLib.TypeModule
-
Router.asterisk
to handle asterisk URI*
- inheritence for
Route
to split concerns - less responsibilities in
Route
subclasses to encapsulate scopes, types and other features inRouter
-
register_type
API for the incoming typed parameters
Status handlers has been reworked and cleaned up to cover more cases using
a switch
block.
- status handlers are executed in the
Router
context rather than a doubletry-catch
for consistent behaviours - the error message is only used for headers that MUST be part of the response, except for redirection codes
Aggressive Optimizations
Okay, let me calm down. The idea is to bring routing in the O(log n)
world by
using a GLib.Sequence
which consist of a binary tree data structure.
Basically, we have a sequence of Route
objects and we try to find in the
least number of attempts the next one that accepts a given request.
In short, here’s what should be done:
- sorting by exclusive criteria (method, version, …)
- sorting by usage
- pre-lookup using a trie-based index
I still need to figure out more, it’s all in issue #144.
Middlewares
With the common ground set, this series will bring useful middlewares to
process Request
and Response
efficiently.
To avoid confusion about what a middleware is, I decided to apply the term only
to HandlerCallback
instances.
Content Negociation
Content negociation is implemented as a set of middlewares which check the request, set the appropriate headers and forward the processing.
If the produced resource is not acceptable, next
is called unless
NegociateFlags.FINAL
is specified. Then, a 406 Not Acceptable
is raised.
app.get ("", accept ("text/html", (req, res) => {
res.body.write_all ("<!DOCTYPE html><html>Hello world!</html>");
}));
app.get ("", accept ("text/plain", (req, res) => {
res.body.write_all ("Hello world!");
}, NegociateFlags.FINAL));
The latests improvements are awaiting tests.
Static Resources Delivery
Static resources can be served from a path or a resource bundle. It support multiple options:
-
ETag
which identify the resource uniquely to prevent transmission -
Last-Modified
(only for path) -
X-Sendfile
if the HTTP server supports it - mark the delivered resource as public for caches
- deliver asynchronously
Delivery from GLib.Resource
defaults on the global resources.
The only requirement is to provide a path
key in the routing context, which
can be easily done with a rule or a regular expression:
using Valum.Static;
app.get ("<path:path>", serve_from_resources (ServeFlags.ENABLE_ETAG));
It’s living here in #143.
Flags for HTTP methods
This is a really nice feature.
HTTP methods are now hanlded as flags in the Router
to perform very efficient
match.
Standard methods are available in Method
enumeration along with the following
symbols:
-
ALL
to capture all standard method -
OTHER
to capture non-standard method -
ANY
to capture any method
If OTHER
is specified, it must be implemented in the matching callback.
all
and methods
have been removed from Router
for obvious reasons and
method
has been renamed to rule
to remain consistent.
app.rule (Method.GET | Method.POST, "", () => {
});
app.rule (Method.ALL, "", () => {
});
app.rule (Method.ANY, "", () => {
});
Method.GET
actually stands for Method.ONLY_GET | Method.HEAD
so that it can
also capture HEAD
requests. It’s pretty handy, but I still need to figure out
how to strip the produced body.
VSGI Loader
More details here: #130.
Loading of application described as a dynamic module (see
GModule
for more details) will be brought by a small utility named vsgi
. It will be
able to spawn instances of the application using a VSGI implementation.
The application has to be written in a specific manner and provide at least one entry point:
public HandlerCallback app;
[CCode (cname = "g_module_check_init")]
public check_init () {
var _app = new Router ();
...
app = _app.handle;
}
[CCode (cname = "g_module_unload")]
public void unload () {
app = null;
}
vsgi --directory=build --server=scgi app:app
All VSGI implementations are loadable and compatible with GLib.TypeModule
.
The application is automatically reloaded on SIGHUP
and it should be possible
to implement live reloading with GLib.FileMonitor
to facilitate the development as well as integration of Ivy
to beautify the stack trace.
Multipart I/O Streams
The multipart stream is essential for any web application that would let clients submit files.
The implementation will be compatible with Soup.MultipartInputStream.
app.post ("", (req, res) => {
var multipart_body = new MultipartInputStream (req.headers, req.body);
InputStream? part;
MessageHeaders part_headers;
while (part = multipart_body.next_part (out part_headers) != null) {
if (part_headers.get_content_disposition ())
}
});
Typed Rule Parameters
The use of GType to interpret and convert rule parameters is essential for an optimal integration with GLib.
The idea is to declare types on the Router
and attempt a conversion before
pushing the parameter on the context.
app.register_type ("int", /\w+/, typeof (int));
app.get ("<int:i>", (req, res, next, ctx) => {
var i = ctx["i"].get_int ();
});
Type conversion can be registered with GLib.Value.register_transform_func
:
Value.register_transform_func (typeof (string),
typeof (int),
(src, ref dest) => {
dest = (Value) int.parse ((string) src);
});
One useful approach would be to reverse that process to generate URLs given a rule.