Without any configuration, awful listens on port 8080. Since awful uses Spiffy behind the scenes, you can configure the web server parameters using Spiffy's.

define-page is the primitive procedure to define pages. In the simplest case, it takes as arguments a path to the page and a procedure to generate the page contents. The path to the page you use as the first argument is the same to be used as the path part of the URL you use to access the page.

In the example we use the main-page-path parameter, which is one of the awful configuration parameters. The default is "/main". So, when you access http://localhost:8080 awful will take you to http://localhost:8080/main.

If you look at the page source code, you'll see that awful created an HTML page for you. Awful uses the html-tags's html-page procedure behind the scenes. You can customize the page either by passing html-page's keyword parameters to define-page or by setting the page-template parameter with your favourite procedure to generate pages (a one argument procedure which receive the page contents and returns a string representing the formatted contents). define-page would still pass the html-page keyword parameters, so you can make use of them.

You can also use some global page-related parameters if all pages use the same CSS, doctype and charset (page-css, page-doctype and page-charset, respectively).

Accessing request variables

Awful provides a procedure ($) to access variables from the request, both from the query string (GET method) and from the request body (e.g., POST method).

Here's a modified "Hello, world!" example to greet some person instead of the whole world:

The ++ procedure is an alias to string-append (name inspired by Haskell's operator to concatenate strings).

So, restart the web server to reload the code, then access the main page using an argument, represented by the person query string variable: http://localhost:8080/main?person=Mario. You'll see a page showing Hello, Mario!.

Re-evaluating the code by reloading the page

When we upgraded our "Hello, world!" example to the improved one which can use an argument passed through the URL, we needed to modify the code and restart the web server to reload the application code. Awful provides a way to reload the code via URL without restarting the server.

By default, the reload URL path is bound to /reload. So, to reload your code, you can access http://localhost:8080/reload.

You can disable this feature by setting the disable-reload? parameter to #t. You can also change the URL path by setting the reload-path parameter.

Using ajax

Awful provides a way to (hopefully) make the use of ajax straightforward for web applications.

By default, the ajax support is disabled, but it can be easily enabled by setting the enable-ajax parameter to #t. When you enable ajax, all the pages defined via define-page will be linked to the JQuery javascript library. The URL of the JQuery file can be customized by setting the ajax-library parameter (the default is Google's API JQuery file version 1.3).

When you have ajax enabled and you want to disable it for specific pages, you can pass #f as the value for the define-page keyword parameter no-ajax.

1. The URL path to the server-side handler (a string). This path is relative to ajax-namespace parameter (default is ajax. So, in the example, we'll have "/ajax/greetings" as the ajax path to be generated if we pass "ajax" as the first argument.

2. The ID of the DOM element to observe.

3. The event to be handled

4. The procedure to be run on the server-side.

So, in the example, ajax will bind the fourth argument (the procedure) to the first argument (the path) on the server side. Then it will add javascript code to the page in order to wait for click events for the element of ID greetings. When we click "Hello, <person>!", we'll get "Hello, awful!" printed on the page as reply. ajax updates the DOM element whose id is the value of the target keyword parameter ("greetings-reply", in the example).

For the very specific case of creating links that execute server side code when clicked, awful provides the ajax-link procedure. So our example could be coded like:

Database access

As with ajax, database access is not enabled by default. To anable it, you need to set the enable-db parameter to #t.

Additionally, to use the db, you need to provide the credentials. You can provide the credentials by setting the db-credentials parameter (see the postgresql egg).

To actually query the database, there's the $db procedure, which uses as arguments a string representing the query and, optionally, a default value to be used in case the query doesn't return any result. $db returns a list of lists. Below is an usage example:

There's also the $db-row-obj procedure for when you want to access the results of a query by row name. $db-row-obj returns a procedure of two arguments: the name of the field and, optionally, a default value to be used in case the field value is #f.

If you need more flexibility to query the database, you can always use the (db-connection) parameter to get the database connection object and use it with the procedures available from the postgresql egg API.

Login pages and session

Awful provides a very basic (awful?) support for creating authentication pages.

The basic things you have to do is:

1. Enable the use of sessions:

(enable-session #t)

2. Set the password validation parameter. This parameter is a two argument procedure (user and password) which returns a value (usually #t or #f) indicating whether the password is valid for the given user. The default is a password which returns #f. Let's set it to a procedure which returns #t is the user and the password are the same:

(valid-password?
(lambda(user password)(equal? user password)))

3. Define a login trampoline, which is an intermediate page accessed when redirecting from the login page to the main page.

(define-login-trampoline"/login-trampoline")

4. Create a login page. Awful provides a simple user/password login form (login-form), which we are going to use. Here's our full example so far (using the basic "Hello, world!" as main page).

That's the very basic we need to set an auth page. If the password is valid for the given user, awful will perform a redirect to the main page (main-page-path parameter) passing the user variable and its value in the query string . If the password is not valid, awful will redirect to the login page (login-page-path parameter)) and pass the following variables and values:

reason

the reason why awful redirected to the login page. It may be invalid-password, for when the password is invalid for the given user; or invalid-session for when the session identifier is not valid (e.g., the session expired).

attempted-page

the URL path to page the user tried to access, but couldn't because either he/she was not loged in or he/she provided an invalid session identifier.

Now we're gonna change our main page to store the user in the session and retrieve it to make the greetings message:

Here we can see the two procedures to access the session: $session and $session-set!.

$session-set! accepts two arguments: the first one is the name of the session variable and the second one is its value.

$session takes the name of the session variable as argument and returns its session value. We can optionally use a second argument to specify a default value, in case the session variable is not bound or is #f.

Session inspector

Awful provides a session inspector, so we can easily see the session contents for a given session identifier. By default, the session inspector is disabled. We can enabled it using the enable-session-inspector procedure, passing the session inspector URL path as argument:

(enable-session-inspector "session-inspector")

Now, if you log in and try to access http://localhost:8080/session-inspector?sid=<paste the sid value here>, you'll get ... an access denied page.

Awful provides a way to control access to the session inspector (session-inspector-access-control parameter). The session-inspector-access-control parameter is an one-argument procedure which returns #f or some other value to indicate whether the access to the session inspector is allowed or not. By default, it blocks all access. Let's configure it so we can access the session inspector from the local machine (whose IP number is 127.0.0.1):

Regarding to the access denied message, you can customize it by setting the session-inspector-access-denied-message.

Now we can access http://localhost:8080/session-inspector?sid=<paste the sid value here> and see the session contents.

Web REPL

For further run-time, server-side web hacking, awful provides a REPL that you can use via web browser. The activation and control access are basically the same as for the session inspector. The relevant procedure and parameters are:

enable-web-repl

web-repl-access-control

web-repl-access-denied-message

Pages access control

To allow/deny access to pages, you can use the page-access-control parameter. It's an one-argument procedure (the page path) which can be set to determine if the access to the page is allowed or not.

The example bellow shows a very silly access control to the main page: it only allows the access when the value of the request variable user is "mario":

<parameter>(ajax-library [string])</parameter>

The default value is "http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"

<parameter>(enable-ajax [boolean])</parameter>

When #t, makes define-page link the ajax-library to the generated page.

The default value is #f

<parameter>(ajax-namespace [string])</parameter>

Name to be used as a namespace for ajax URL paths.

The default value is "ajax".

<parameter>(enable-session [boolean])</parameter>

When #t, session support is enabled.

The default value is #f.

<parameter>(page-access-control [procedure])</parameter>

An one-argument (URL path of the current page) procedure which tells whether the access to the page is allowed or not.

The default value is (lambda (path) #t).

<parameter>(page-access-denied-message [procedure])</parameter>

An one-argument (URL path of the current page) procedure which returns the access denied message.

The default value is (lambda (path) (<h3> "Access denied.")).

<parameter>(page-doctype [string])</parameter>

The doctype (see the doctype egg) to be applied to all pages defined by define-page. Can be overwritten by define-page's doctype keyword parameter.

The default value is "".

<parameter>(page-css [boolean or string])</parameter>

The CSS file to be linked by all pages defined by define-page. Can be overwritten by define-page's css keyword parameter. See html-utils's html-page procedure to know about the css keyword parameter syntax.

The default value is #f (no CSS).

<parameter>(page-charset [boolean or string])</parameter>

The page charset to be used by all pages defined by define-page. Can be overwritten by define-page's charset keyword parameter. The default value is #f (no explicit charset).

<parameter>(login-page-path [string])</parameter>

The URL path for the login page. When creating a login page, be sure to set the no-session keyword parameter for define-page to #t, otherwise you'll get an endless loop.

The default value is "/login".

<parameter>(main-page-path [string])</parameter>

The URL path to the app main page.

The default value is "/main".

<parameter>(app-root-path [string])</parameter>

The base path to be used by the application. All the pages defined by define-page will use app-root-path as the base directory. For example, if app-root-path is set to "/my-app" and "my-page" is used as first argument to define-page, the page would be available at http://<server>:<port>/my-app/my-page.

The default value is "/".

<parameter>(valid-password? [procedure])</parameter>

A two-argument (user and password) procedure which indicates whether the given password is valid for the given user.

The default value is (lambda (user password) #f).

<parameter>(page-template [procedure])</parameter>

An one-mandatory-argument procedure to be used by define-page (unless define-page's no-template keyword parameter is set to #f) to generate HTML pages. Although this procedure can take only one mandatory argument, the following keyword arguments are passed:

<parameter>(disable-reload? [boolean])</parameter>

List of read-only parameters available to users

Note: these parameters should not be explicitly set and when their use is needed, it's a string sign you're doing something you shouldn't (except for db-connection, which can be used by procedures from the postgresql egg API).

<parameter>(http-request-variables)</parameter>

<parameter>(db-connection)</parameter>

A per-request postgresql egg connection object. The connection is automatically opened and closed by awful in a per-request basis (unless enable-db is #f or the no-db keyword parameter for define-page is #t.)

<parameter>(page-javascript)</parameter>

Javascript code to be added to the pages defined by define-page.

<parameter>(sid)</parameter>

The session identifier.

List of procedures

<procedure>(++ string1 string2 ... stringn)</procedure>

A shortcut to string-append.

<procedure>(concat args #!optional (sep ""))</procedure>

Convert args to string and intersperse the resulting strings with sep.

<procedure>(include-javascript file)</procedure>

A shortcut to (<script> type: "text/javascript" src: file).

<procedure>(add-javascript . code)</procedure>

Add arbitrary javascript code to the pages defined by define-page.

<procedure>(debug . args)</procedure>

Print args, concatenated, to the file debug-file.

<procedure>(debug-pp arg)</procedure>

Pretty-print arg to the file debug-file.

<procedure>($session var #!optional default)</procedure>

Return the value of var in the session (or default if var does not exist or is #f).

<procedure>($session-set! var #!optional val)</procedure>

If var is a quoted symbol, set the value of var to val. If val is not provided, var will have its value set to #f.

var can be an alist mapping session variable names to their corresponding values.

Generate javascript code to be added to the page defined by define-page. Return the generated javascript code (which usually is not useful, so should be discarded).

path is the URL path (a string) of the server side handler. This path is placed under the (app-root-path)/(ajax-namespace) path. So, if your app-root-path is "my-app", your ajax-namespace is "ajax" and you use "do-something" as the first argument to ajax, the URL for the server side handler would be "/my-app/ajax/do-something".

id (a quoted symbol) is the id of the DOM element to be observed.

event (a quoted symbol or a list) is the event(s) to be observed. If it is a quoted symbol (e.g., 'click), only this event will be bound. If event is a list of events, all the events from the list will be bound.

proc is a no-argument procedure to be executed on the server side.

The target keyword parameter is the id of the DOM element to be affected by the result of proc.

The method (a quoted symbol, usually 'GET or 'POST) keyword parameter is the HTTP method to be used by the ajax request.

The arguments keyword parameter is an alist mapping request variables (symbols) to their values (strings). ajax uses these arguments to assembly the query string or the request body to send to the server when performing the ajax request.

Example:

arguments: '((var1 . "$('#var1').val()")(var2 . "$('#var2').val()"))

If the no-session keyword parameter is #t, it means that no session should be considered (ajax implicit sends the session identifier when no-session is #f).

If the no-db keyword parameter is #t, it means that the should be no attempt to connect the database, even when enable-db is #t and db-credentials is not #f.