Git Product home page Git Product logo

valum's Introduction

Valum Web micro-framework

Build Status Documentation Status codecov.io

Valum is a Web micro-framework entirely written in the Vala programming language.

using Valum;
using VSGI;

var app = new Router ();

app.use (basic ()); /* handle stuff like 404 errors and more */

app.get ("/", (req, res) => {
    res.headers.set_content_type ("text/plain", null);
    return res.expand_utf8 ("Hello world!");
});

Server.@new ("http", handler: app).run ({"app", "--address=0.0.0.0:3003", "--forks=4"});

Installation

Docker

We maintain Docker images already setup with Valum and the latest LTS version of Ubuntu.

docker pull valum/valum

Bower

If you use Meson, you can install Valum as a subproject using Bower:

bower install valum

For other installation procedures, head to the user documentation.

Features

Valum has a two layer architecture: VSGI a middleware that abstract away various network protocols under a simple interface and Valum itself, a Web micro-framework that provide all the features needed for writing applications and services. In short it provides:

  • powerful routing mechanism to write expressive Web services:
    • helpers and flags (i.e. Method.GET | Method.POST) for common HTTP methods
    • scoping
    • rule system supporting typed parameters, group, optional and wildcard
    • regular expression with capture extraction
    • automatic HEAD and OPTIONS
    • subrouting
    • status codes through error domains (i.e. throw new Redirection.PERMANENT ("http://example.com/");
    • context to hold states
  • middlewares for subdomains, server-sent events, content negotiation and much more
  • VSGI, an abstraction layer for various protocols:
    • fast, asynchronous and elegant
    • streaming-first API
    • listen on multiple interfaces (e.g. port, UNIX socket, file descriptor) with tight GIO integration
    • support libsoup-2.4 built-in HTTP server, CGI, FastCGI and SCGI out of the box
    • support plugin for custom server implementation
    • fork to scale on multi-core architecture
    • cushion for parsing CLI, logging and running a Web application
  • extensive documentation at valum-framework.readthedocs.io

Contributing

Valum is built by the community under the LGPL license, so anyone can use or contribute to the framework.

  1. fork repository
  2. pick one task from TODO.md or GitHub issues
  3. let us know what you will do (or attempt!)
  4. code
  5. make a pull request of your amazing changes
  6. let everyone enjoy :)

We use semantic versioning, so make sure that your changes

  • does not alter api in bugfix release
  • does not break api in minor release
  • breaks api in major (we like it that way!)

Discussions and help

You can get help with Valum from different sources:

  • mailing list: vala-list.
  • IRC channel: #vala and #valum at irc.gimp.net
  • issues on GitHub with the question label

valum's People

Contributors

aguspiza avatar antono avatar arteymix avatar bob131 avatar chebizarro avatar colinkiama avatar corpsee avatar efrane avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

valum's Issues

Recipes for the framework

I think it would be a nice idea to document good practices we learn building Valum.

Right now, I have a few ideas:

  • bundling static resources
  • work with stream (splice is awesome)
  • custom matcher and catch-all
  • deployment using RPM
  • systemd and syslog

It's possible to literally deploy an app using a package manager and it's quite appropriate. Maybe cover workers? and replacement strategy?

Stream Ctpl view

Ctpl streams its output, which is useful if we have to produce big files that we do not want to store directly in memory.

libsoup also support buffered Soup Message.

These two combined could be quite amazing. We could stream out a very big XML file templated in Ctpl for example :)

Cookies should not be filtered by the Router

It's the role of the client to provide valid cookies to the server in its request.

Moreover, the check does absolutely nothing since no information is providen outside the key and the
value in a Cookie header.

Enable polling for FastCGI streams

The actual implementation relies on the fallback threading of GIO, but considering that we have a GLib.Socket and we can capture I/O conditions, it would be really great to implement write_async and other asynchronous functions properly.

Extract captures from Route regex

It is more convenient to extract the named captures than require them to be providen in Route constructor.

Right now, it is not possible to declare a route using a Regex and expect captures to work properly.

Build both static and dynamic library

waf is a bit buggy if you want to build both static and library from the same set of sources, but I think it should be possible to choose wether you want a static or dynamic build

We should have two documented way of building with Valum:

  • locally using a static build
  • against the system shared library

Only the shared library should be installed when ./waf install is called.

Integrate FastCGI

Vala has bindings for FastCGI, so I think we could integrate it for now until we get a complete and working SGI solution.

We have to abstract the Request and Response objects to interfaces.

I have a few ideas:

  • use abstract properties (preserves current interfaces)
  • Request body can be an InputStream
  • Response body can be an OutputStream

We can even base a future VSGI on this implementation and extract the interfaces we get from working them out on Valum.

Middlewares

Middleware can be implemented in a dead-simple manner.

Functions that respect the Route.RouteCallback signature are valid middlewares, absolutely no code change involved.

We could predefine some useful middlewares in a static class or as a set of static function in a namespace that user could bind to specific rules, regex or matcher.

Express.js has some nice middlewares we could make use of: https://github.com/senchalabs/connect?_ga=1.268126861.268308174.1424308974#middleware

  • serve GLib.Resource
  • serve static files
  • authentication?
  • RESTful api generation?

Use flags to represent HTTP methods

This is just an idea, but we could do really interesting stuff if our HTTP methods were represented using flags.

app.method (Request.GET | Request.POST, "", (req, res) => {
    // match GET and POST
});

The Request method would still be a string, but the router would use flags to resolve matching methods.

This, or an array of string, which would be backward compatible:

app.methods ({Request.GET, Request.POST}, "", (req, res) => {
    // match GET and POST
});

app.all ("", (req, res) => {
    // match all HTTP methods by calling app.methods (Request.METHODS)
});

Produce a documentation

Read the Docs is a nice service for hosting documentation and would only require to setup a GitHub hook.

I think that we should produce a user guide in Markdown and an api documentation using the valadoc tool.

@antono if you could enable the hook in the repo settings, it would be amazing!

Use hold and release to count processing requests and keep the server alive

GLib.Application can be held and released in a way that it keeps running as long as it has a reason to.

Using hold & release per incoming request guarantee a nice running model where:

  • running process timeout when no more requests are received
  • set_inactivity_timeout can be used to define that timeout

If there is a possibility to obtain the hold count, we could measure how many requests are being processed at a given time.

The release should be done whenever a response object is collected by the GC, since we have to keep the application alive to process asynchronous operations.

Accessing with a wrong method should return a 405

When trying to access a an URL that is only mapped to a specific method with another method (e.g. GET /index/ is mapped, trying POST /index/) a 405 Method Not Allowed should be returned. At the moment a 404 File Not Found is returned which is not exactly what's wrong.

Reverse a Route path from its rule

I already have a branch that prototypes that feature and a few ideas.

I cannot figure out how this could be done from a regex, but a rule yes. We could reuse the rule splitting code to generate a reversed path for a route.

  •  from varidic arguments
  • form a HashTable

Also, we can either have named or referenced route. My prototype uses referenced; the only issue being that you can't reference an undeclared route.

HashTable is useful if the data is computed:

var home = app.get ("home/<id>", (req, res) => {});

var params = new HashTable<string, string> ();
params["id"] = "4";
var path = home.get_path (params);

If you need convenience, the varidic argument (not implemented yet..) should do the trick:

// assume precedent home declaration

home.get_pathv ("4");

@antono I need some reviews and thoughts on this.

Request and Response filtering

Similarly to stream filtering, we could provide base classes to build request and response filters so that we can buffer or compress them seamlessly.

At Valum level, the NextCallback can pass a filtered request or response to the next handler in the queue, which leans to really nice applications:

app.get ("", (req, res) => {
    // next handler will have its response buffered
    next (req, new BufferedResponse (res, 2048));
});

Problems with libsoup

I'm using new version of libsoup, Seems like Valum treat libsoup in old way and use old SoupServer Listening API. Am I wrong?
https://developer.gnome.org/libsoup/stable/SoupServer.html#soup-server-listen
Default port to listen can not be changed.
There are build time errors also:
valum/src/status.vala:77.32-77.37: error: The name 'Status' does not exist in the context of 'Valum.Route.fire'
valum/src/router.vala:44.18-44.28: error: The name 'Status' does not exist in the context of
'Soup'
valum/src/vsgi/fastcgi.vala:135.61-135.71: error: The name 'Status' does not exist in the context of 'Soup'

Move the scoping to the Route

Right now, the Router has the responsibility to scope a rule before passing it to the Route.

If we scope in the Route, we can scope regular expression and probably use that information in custom matcher.

I see two approaches:

  • Router.scope become public, same implementation
  • Route become a inner class of Router and we can keep Router scope private (but more complex code)

Improve documentation code coloring

The code coloring in the documentation is really bad for Vala.

Sphinx is much better, but it's a little bit more of configuration and we would have to rewrite the documentation in rst.

Fully asynchronous operations

This is the next step for Valum once we will have a nice API.

InputStream and OutputStream already have the primitives for asynchronous operations, so we got to make them work with the request model. The FastCGI support enable the use of Request and Response as streamed objects.

Base VSGI.Server on GLib.Application

The server could benefit a lot of inheriting from GLib.Application

  • ability to parse command line arguments
  • built-in MainLoop
  • ability to declare concurrent task (sending mails, cleaning the cache, etc...)
  • better integration in general with the OS

I have a prototype and I will get it working, but the changes are minor.

Server.listen will become Server.run since it is how we start a GLib.Application instance.

Support multipart/* messages

libsoup-2.4 provides facilities to deal with multipart/* messages, although it might not integrate very well with the other VSGI implementations.

It could be implemented with a MultipartInputStream and MultipartOutputStream that would provide similar a similar API to MultipartInputStream.

MessageHeadersType.MULTIPART can be used to produce headers for a part.

The MultipartInputStream implementation would be identical in terms of API to the libsoup-2.4 one, outside maybe the asynchronous part.

For the MultipartOutputStream, a typical session would be:

app.get ("", (req, res) => {
    var multipart = new MultipartOutputStream (res.body);

    var part_headers = new MessageHeaders (MessageHeadersType.MULTIPART);

    // filters the body OutputStream
    var part_body = multipart.new_part (part_headers);

    part_body.write ("Hello world!");

    res.end ();
});

Signals in VSGI

VSGI should define some signals on Request, Response and Server specifications.

The Application already define the handler signal and I think it's enough so far.

Node.js specifications should provide us with all the information we need to build a nice signal API http://nodejs.org/api/http.html

Subrouting

Subrouting is the possibility to invoke another router to handle a pair of request and response.

var app = new Router ();
var api = new Router ();

app.get ("", (req, res) => {
    api.handle (req, res);
});

// or simpler since handle is type compatible with Route.Handler
app.get ("",  api.handle);

This feature requires the handle function to be synchronous, so we can just provide a handle_async for asynchronous processing in VSGI.Application.

it requires a bit of documentation.

FastCGI.request.accept is blocking and causes a deadlock

Accepting a FastCGI request is a blocking operation (blocks until a new request record is available on the file descriptor). The current code will block on accepting and no further processing will occur, resulting in a dead lock.

There's two approach we might take:

  • accept in a thread
  • accept in a non-blocking way (by peeking the available record)

I don't know how it can be possible to peek the available data on a socket, so it might just be simpler to accept using a thread, maybe a ThreadPool.

Keep the core clean

I think Valum should provide only basic routing and request-response.

We would gain much more from having a slick micro-framework upon which anyone could build the full-featured framework he likes. This is how Node.js work: by providing basic abstraction and letting framework like Express.js handle application development.

The key part of Valum would be to provide a awesome routing system and integration in CGI, SGI, and other web server implementation.

We should move the templating facilities, scripting engines and all other fancy stuff in separate libraries that end-developer could or not include in its web application.

However, I really see from a good eye to have an included templating solution like Ctpl, so we should ship it with Valum.

Ultimately, a Valum application would be combined with other C/Vala libraries to handle database persistency, Redis or any external services.

The generated header file should not have the API version in its name

Right now, the generated header valum-0.1.h has the API version, but this information is already included in the folder.

Either we keep the folder or install the file in the include folder with the API version.

The advantage of using a folder is that it makes cleaner install files if we have multiple headers, but since this is a micro-framework, maybe having a single valum.h file is sufficient?

Session

Cookies are working, so we should look forward session implementation.

The main issue about it is its persistency. Most web servers offers that service, so I believe it's best that we do not implement the session storage ourselves.

Now I think we should gather information about server-side session implementation to take the right design decision.

Redesign asynchronous model

I have been thinking alot recently about how we can improve the asynchronous model of a Valum application.

I came to the conclusion that relying on reference counting to free the request & response is not sufficient: we still need the response data when it's finalized. Thus, we have to explicitly terminate the processing during the flow of the handler, especially if it happens in a async callback.

What we can do is adding a end signal in the Response object that has to be invoked somewhere in the processing to notify that the response is completed and that any related resources should be freed.

Also, implementing InputStream and OutputStream is bad: wrapping a stream is not sufficient in most cases to provide full-fledged features. Some streams implementation use polling for asynchronous operations and this is not enabling these really nice capabilities. We have to provide a body attribute or property.

Now, what it does mean:

  • VSGI implementations can teardown the resources required to serve a client by connecting a callback to end
  • application handler has to invoke end since it can keep processing the response after it returns

I have a prototype for all this in the 0.2/redesigned-async-model branch.

I think that this could be the nerve of a future release and I'll try to dig more on the matter.

Handle HTTP status using an exception mechanism

It would be nice to do something like:

app.get ("", (req, res) => {
    throw Soup.Status.NOT_FOUND ("");
});

app.error (Soup.Status.NOT_FOUND, (req, res, error) => {
    // handle error...
});

This could facilitate error handling and production of appropriate response.

Use native regex in the types map

Right now, we have to use a string to represent a regex piece.

app.types["int"] = /-?\d+/

It is much more flexible to use regex literal than string.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.