Skip to content

Commit

Permalink
Use an abstract class to represent VSGI applications
Browse files Browse the repository at this point in the history
Remove the 'ApplicationCallback' delegate in favour of 'Handler', an
abstract class. This is to make VSGI more easily implementable.

Add 'HandlerInitFunc' and 'HandlerModule' to load VSGI handler
dynamically. Unlike servers, these are user-provided and not found in
common locations, so we don't need any constructors.

Remove 'Server.new_with_application' in favour of 'Server.new (handler: app)'
syntax.

Update docs accordingly and cover dynamically loadable handlers.
  • Loading branch information
arteymix committed Oct 27, 2016
1 parent f2dfe36 commit b4edfb9
Show file tree
Hide file tree
Showing 34 changed files with 277 additions and 205 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ app.get ("/", (req, res) => {
return res.extend_utf8 ("Hello world!");
});
Server.new_with_application ("http", app.handle).run ({"app", "--forks=4"});
Server.@new ("http", handler: app).run ({"app", "--forks=4"});
```


Expand Down
14 changes: 2 additions & 12 deletions docs/application.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,11 @@ will be served with :doc:`vsgi/server/http`.

::

Server.new_with_application ("http", app.handle).run ({"app", "--port", "3003"});
Server.new ("http", handler: app).run ({"app", "--port", "3003"});

:doc:`vsgi/server/index` takes a server implementation and an
``ApplicationCallback``, which is respected by the ``handle`` function.

Minimal application can be defined using a simple lambda function taking
a :doc:`vsgi/request` and :doc:`vsgi/response`.

::

Server.new_with_application ("http", (req, res) => {
res.status = 200;
return res.expand_utf8 ("Hello world!");
}).run ({"app", "--port", "3003"});

Usually, you would only pass the CLI arguments to ``run``, so that your runtime
can be parametrized easily, but in this case we just want our application to
run with fixed parameters. Options are documented per implementation.
Expand All @@ -79,6 +69,6 @@ run with fixed parameters. Options are documented per implementation.

// assume some route declarations...

Server.new_with_application ("http", app.handle).run (args);
Server.new ("http", handler: app).run (args);
}

2 changes: 1 addition & 1 deletion docs/getting-started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ the latest changes in the framework.
return res.expand_utf8 ("Hello world!");
});

Server.new_with_application ("http", app.handle).run ({"app", "--port", "3003"});
Server.new ("http", handler: app.handle).run ({"app", "--port", "3003"});

Typically, the ``run`` function contains CLI argument to make runtime the
parametrizable.
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/persistence.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ maintained in nemequ/vala-extra-vapis GitHub repository.
return res.expand (value, null);
});

Server.new_with_application ("http", app.handle).run ();
Server.new ("http", handler: app).run ();
2 changes: 1 addition & 1 deletion docs/recipes/scripting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Lua
return res.expand_utf8 (lua.do_file ("scripts/hello.lua"));
});

Server.new_with_application ("http", app.handle).run ();
Server.new ("http", handler: app.handle).run ();

The sample Lua script contains:

Expand Down
26 changes: 10 additions & 16 deletions docs/vsgi/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,21 @@ pattern is highlighted in the following example:

::

using VSGI;
var authentication = BasicAuthentication ("realm");

Server.new_for_application ("http", (req, res) => {
var authentication = BasicAuthentication ("realm");
var authorization_header = req.headers.get_one ("Authorization");

var authorization_header = req.headers.get_one ("Authorization");

if (authorization_header != null) {
if (authentication.parse_authorization_header (authorization_header,
out authorization)) {
var user = User.from_username (authorization.username);
if (authorization.challenge (user.password)) {
return res.expand_utf8 ("Authentication successful!");
}
if (authorization_header != null) {
if (authentication.parse_authorization_header (authorization_header,
out authorization)) {
var user = User.from_username (authorization.username);
if (authorization.challenge (user.password)) {
return res.expand_utf8 ("Authentication successful!");
}
}
}

res.headers.replace ("WWW-Authenticate", authentication.to_authenticate_header ());

return res.end ();
}).run ();
res.headers.replace ("WWW-Authenticate", authentication.to_authenticate_header ());

Basic
-----
Expand Down
17 changes: 6 additions & 11 deletions docs/vsgi/connection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@ require the status to be part of the response headers.

.. code::
using VSGI;
Server.new_with_application ("http", (req, res) => {
var message = req.connection.output_stream;
message.write_all ("200 Success HTTP/1.1\r\n".data. null);
message.write_all ("Connection: close\r\n");
message.write_all ("Content-Type: text/plain\r\n");
message.write_all ("\r\n".data);
message.write_all ("Hello world!".data);
return true;
});
var message = req.connection.output_stream;
message.write_all ("200 Success HTTP/1.1\r\n".data. null);
message.write_all ("Connection: close\r\n");
message.write_all ("Content-Type: text/plain\r\n");
message.write_all ("\r\n".data);
message.write_all ("Hello world!".data);
7 changes: 2 additions & 5 deletions docs/vsgi/converters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ using the ``convert`` method.

::

Server.new_with_application ("http", (req, res) => {
res.headers.append ("Content-Encoding", "gzip");
res.convert (new ZlibCompressor (ZlibCompressorFormat.GZIP));
return res.expand_utf8 ("Hello world!");
});
res.headers.append ("Content-Encoding", "gzip");
res.convert (new ZlibCompressor (ZlibCompressorFormat.GZIP));

.. warning::

Expand Down
75 changes: 60 additions & 15 deletions docs/vsgi/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,33 @@ library.
VSGI produces process-based applications that are able to communicate with
various HTTP servers using standardized protocols.

Entry point
-----------
Handler
-------

The entry point of a VSGI application is type-compatible with the
``ApplicationCallback`` delegate. It is a function of two arguments:
a :doc:`request` and a :doc:`response` that return a boolean indicating if the
request has been or will be processed.
The entry point of any VSGI application implement the ``Handler`` abstract
class. It provides a function of two arguments: a :doc:`request` and
a :doc:`response` that return a boolean indicating if the request has been or
will be processed. It may also raise an error.

::

using VSGI;

Server.new_with_application ("http", (req, res) => {
// process the request and produce the response...
return true;
}).run ();
public class App : Handler {

If an application indicate that the request has not been processed, it's up to
the server implementation to decide what will happen.
public override handle (Request req, Response res) throws Error {
// process the request and produce the response...
return true;
}
}

Server.new ("http", handler: new App ()).run ();

If a handler indicate that the request has not been processed, it's up to the
server implementation to decide what will happen.

From now on, examples will consist of ``Handler.handle`` content to remain
more concise.

Error handling
~~~~~~~~~~~~~~
Expand All @@ -51,9 +59,7 @@ which will in turn teardown the connection appropriately.

::

Server.new_with_application ("http", (req, res) => {
throw new IOError.FAILED ("some I/O failed");
});
throw new IOError.FAILED ("some I/O failed");

Asynchronous processing
~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -89,3 +95,42 @@ done as the following example demonstrates:
var written = res.body.write_async.end (result);
});

Dynamic loading
~~~~~~~~~~~~~~~

.. versionadded:: 0.3

It could be handy to dynamically load handlers the same way
:doc:`server/index` are.

Fortunately, this can be performed with the ``HandlerModule`` by providing
a directory and name for the shared library containing a dynamically loadable
application.

::

var module = var new HandlerModule ("<directory>", "<name>");

Server.new ("http", handler: Object.new (module.handler_type)).run ();

The only required definition is a ``handler_init`` symbol that return the type
of some ``Handler``. In this case, the library should be located in ``<directory>/lib<name>.so``,
although the actual name is system-dependant.

::

[ModuleInit]
public Type handler_init (TypeModule type_module) {
return typeof (App);
}

public class App : Handler {

public bool handle (Request req, Response res) {
return res.expand_utf8 ("Hello world!");
}
}

Eventually, this will be used to provide a utility to run arbitrary
applications with support for live-reloading.

15 changes: 3 additions & 12 deletions docs/vsgi/request.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ and can be accessed from the ``headers`` property.

::

Server.new_with_application ("http", (req) => {
var accept = req.headers.get_one ("Accept");
return true;
});
var accept = req.headers.get_one ("Accept");

libsoup-2.4 provides a very extensive set of utilities to process the
information contained in headers.
Expand Down Expand Up @@ -129,10 +126,7 @@ freeing a file resource.

::

Server.new_with_application ("http", (req, res) => {
var payload = req.flatten ();
return true;
});
var payload = req.flatten ();

Form
~~~~
Expand All @@ -142,10 +136,7 @@ format, which is submitted by web browsers.

::

Server.new_with_application ("http", (req, res) => {
var data = Soup.Form.decode (req.flatten_utf8 (out bytes_read));
return true;
});
var data = Soup.Form.decode (req.flatten_utf8 (out bytes_read));

Multipart body
~~~~~~~~~~~~~~
Expand Down
30 changes: 7 additions & 23 deletions docs/vsgi/response.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ implementation to call it at a proper moment.

::

Server.new_with_application ("http", (req, res) => {
res.status = Soup.Status.MALFORMED;
return true;
});
res.status = Soup.Status.MALFORMED;

Reason phrase
-------------
Expand All @@ -36,11 +33,8 @@ status line if ``write_head`` or ``write_head_async`` is invoked.

::

Server.new_with_application ("http", (req, res) => {
res.status = Soup.Status.OK;
res.reason_phrase = "Everything Went Well"
return true;
});
res.status = Soup.Status.OK;
res.reason_phrase = "Everything Went Well"

To obtain final status line sent to the user agent, use the ``wrote_status_line``
signal.
Expand All @@ -61,11 +55,7 @@ from the ``headers`` property.

::

Server.new_with_application ("http", (req, res) => {
res.status = Soup.Status.OK;
res.headers.set_content_type ("text/plain", null);
return res.body.write_all ("Hello world!".data, null);
});
res.headers.set_content_type ("text/plain", null);

Headers can be written in the response synchronously by invoking
``write_head`` or asynchronously with ``write_head_async``.
Expand Down Expand Up @@ -134,9 +124,7 @@ stream and close it properly.

::

Server.new_with_application ("http", (req, res) => {
res.expand_utf8 ("Hello world!");
});
res.expand_utf8 ("Hello world!");

Filtering
~~~~~~~~~
Expand Down Expand Up @@ -212,12 +200,8 @@ provided:

::

Server.new_with_application ("http", (req, res, next) => {
res.status = Soup.Status.NO_CONTENT;
return res.end () && next ();
}).then ((req, res) => {
// perform blocking operation here...
});
res.status = Soup.Status.NO_CONTENT;
res.end ();

To produce a message before closing, favour ``extend`` utilities.

19 changes: 12 additions & 7 deletions docs/vsgi/server/cgi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ necessary.

::

Server.new_with_application ("cgi", (req, res) => {
Timeout.add (5000, () => {
res.expand_utf8 ("Hello world!");
return Source.REMOVE;
});
return true;
}).run ();
public class App : Handler {

public override bool handle (Request req, Response res) {
Timeout.add (5000, () => {
res.expand_utf8 ("Hello world!");
return Source.REMOVE;
});
return true;
}
}

Server.new ("cgi", handler: new App ()).run ();

lighttpd
--------
Expand Down
4 changes: 1 addition & 3 deletions docs/vsgi/server/http.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ test your application or spawn workers in production.

using Valum;

Server.new_with_application ("http", (req, res) => {
return res.expand_utf8 ("Hello world!");
}).run ({"app", "--port", "3003"});
var https_server = Server.new ("http", https: true);

Parameters
----------
Expand Down
Loading

0 comments on commit b4edfb9

Please sign in to comment.