*Project* Anubis
*Title* Making interactive Web sites.
*Copyright* Copyright (c) Alain Prouté 2004-2005.
Copyright (c) Calexium 2007.
*Authors* Alain Prouté
David René
*Revised* May 2007
*Overview*
In this file we propose simple tools for making well structured interactive and secured
web sites.
----------------------------------- Table of Contents ---------------------------------
* (1) Structure of a web site.
** (1.1) Three sorts of data.
** (1.2) How requests are handled.
** (1.3) What web pages are made of.
** (1.4) Actions.
** (1.5) States.
* (2) Carrying on.
** (2.1) Describing your web sites.
** (2.2) Directories on the server's disk.
** (2.3) Starting your web sites.
* (3) The HTML interface.
** (3.1) Types used by the HTML interface.
** (3.2) ``in form'' versus ``off form''.
** (3.3) Defining your own style.
** (3.4) Actioners and forms.
** (3.5) Local popup.
---------------------------------------------------------------------------------------
read tools/basis.anubis
read system/logger.anubis
read CXM_common.anubis
read CXM_multihost_http_server.anubis
read CXM_mime.anubis
* (1) Structure of a web site.
First of all we need to explain what a web site should be made of. Ideally, the
visitor (also called the 'client') should see the web site working as any other
interactive computer software. So, it should be clear that a 'session' (i.e. a visit
to the web site, including the consultation of several pages) is some kind of
conversation between the visitor and the web site, and that the web site should
maintain a 'current state' of this conversation. At each new request (click) from the
visitor, this state must be updated. This whole conversation is called a 'session' and
should not be confused with a single request.
** (1.1) Three sorts of data.
All the data needed for putting a web site at work may be dispatched into three
categories:
1. Constant data (data that never change). These data may be hard coded into the
Anubis source files of the web site.
2. Permanent data (data which always exist independantly of the users connected to
the web site). These data are normally recorded into data bases.
3. Session data (data which depend on a particular visitor and which exist only
during the time he visits the web site). These data are stored into so-called
'states'.
It is important to determine which data belongs to which category. This is part of your
design decisions.
** (1.2) How requests are handled.
We want to separate the following two functionalities (which are used at each request
(click) during a single session):
- computing the new state from the previous state and from the client request, and
updating the data base,
- computing the page to be sent to the client from the new current state and from
the informations in the data base.
The next picture shows the structure we have in mind:
request +---------+ HTML page (with a hidden state name)
.-------------------| client |<--------------.
| .-----------------| | |
| | previous state +---------+ |
| | name (if any) |
| | | client side
............................................................................
| | | server side
| | |
| | .-------------------. |
| | | previous state | |
V V V | |
+---------------+ +---------------+ +--------------+
| compute state | | server's disk | | compute page |
+---------------+ +---------------+ +--------------+
^ | | ^ ^ ^ ^ ^
| | | | | | | |
| | `--------------------+--------------------' | |
| | new state | | |
read | `------------------------+--------------------' |
write | new state name |
update V |
+-----------+ |
| data base |--------------------------------------------'
+-----------+ read only
When the client begins a session, there is no previous state. In this case, a default
'initial state' is used instead.
The data base may be updated by 'compute state' box, but should not be update by the
'compute page' box. The 'compute page' box should be allowed only to read the data
base.
In this file, all the above stuff is defined, except the 'compute state' and 'compute
page' boxes. You just have to provide the function for computing a new state (compute
state) and the function for computing the page (compute page) from the new state. You
don't have to worry about state names, saving and retrieving states and the like.
** (1.3) What web pages are made of.
What the client can see in his browser's window may be called a 'page'. Within a page,
we have several sorts of components:
- 'local' components, i.e. all components which do not open a connection, like
texts, images, etc... possibly using JavaScript programmation,
- 'actioners', which, when clicked upon, open a connection with our web site; they
may appear as links or buttons, etc...
- 'foreign links', which when clicked upon, open a connection with another web site
(or ours eventually).
Of course, what an actioner does is just ask our web site to perform an action. To that
end, the actioner essentially sends the name of the action to be performed. However, it
may be necessary to provide additional informations which may be seen as 'operands' of
the action. In order to attach operands to an action, HTML provides the notion of
'form'. Indeed, a form contains essentially a set of input fields into which the client
may put values for the required operands of the action, and a submit button, which is
the actioner itself. Notice that a single form may contain several submit buttons,
which simply means that there are several distincts actions taking the same set of
operands.
Restrictions must be put on the use of all theses gadgets. Indeed, for example,
putting a form within another form is officially meaningless in HTML, and the client's
browser may be seriously disturbed by this. In this file, we propose an interface to
the HTML language, which forbids such meaningless things, simply by imposing a strict
typing of HTML concepts.
Each web site may be accessible through two communication channels:
- a non secured channel (HTTP),
- a secured channel (HTTPS).
Nevertheless, the whole thing should be considered as a single web site. For example,
you may have a secured page, obtained through HTTPS, containing public images obtained
through HTTP. An actioner in a non secured page may open a secured connection, and
conversely.
Summarizing, a web page is made of local elements, foreign links and actioners.
Actioners receive operands from forms, and they also choose to communicate through the
non secured or through the secured channel.
+-------------------+
| page |
| | +---------------+
| +--------------+ | | next page |
| | form | | | (non secured) |
| | +----------+ | | HTTP | |
| | | actioner |---------------------------->| |
| | +----------+ | | +---------------+
| | | |
| | +----------+ | | +---------------+
| | | actioner |---------------------------->| next page |
| | +----------+ | | HTTPS | (secured) |
| | | | | |
| +--------------+ | | |
| | +---------------+
| |
+-------------------+
Notice that actioners need no be necessarily put into forms. In that case, they work as
ordinary links, but they still may receive operands as we shall see.
** (1.4) Actions.
The client opens a new connection with our web site whenever he clicks on an
actioner. The result is that a request is sent, essentially made of a list of 'web
arguments'. Each web argument is a pair (name,value). One of these web arguments, the
'action' web argument (whose name is "a"), determines the action to be performed. The
other web arguments (not including "s", used to identify the state) are the operands
for this action.
Hence, the 'compute state' box in the picture above, splits naturally into as many
sub-boxes as there are actions. For this reason, we define the following type for
representing actions (where '$State' is the type representing session informations):
public type Web_Action($SessionTicket, $State):
http_action (String name, // name of action
(Maybe($State)) -> Bool allow, // true if action allowed
(HTTP_Info http_info,
List(Web_arg) web_args, // actually only 'operands' web arguments
Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
https_action (String name, // name of action
(Maybe($State)) -> Bool allow, // true if action allowed
(HTTP_Info http_info,
List(Web_arg) web_args, // actually only 'operands' web arguments
Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it),
http_https_action (String name,
(Maybe($State)) -> Bool allow, // true if action allowed
(HTTP_Info http_info,
List(Web_arg) web_args,
Maybe($State) state) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) do_it).
'http_action's are executed only under HTTP, and 'https_action's are executed only
under HTTPS. 'http_https_action's may be executed under both types of connections.
Each action has a name, which is used to identify the action. Each action also has a
function 'allow' whose job is to verify that the action is allowed in the current
state, and a function 'do_it' for performing the action. The function 'do_it' receives
a lot of informations:
- 'HTTP informations':
- the IP address of the client,
- the URI requested by the client (after redirection),
- the list of HTTP headers generated by the client's browser,
- the list of web arguments sent by the client (except "s" and "a"),
- the previous state (or the 'initial' or 'ticket expired' state if no previous
state can be found).
In most cases, HTTP informations are not used. This is the reason why they are gathered
for simplicity into a unique datum of type 'HTTP_Info'.
// For your convenience, we introduce the following simpler variants:
//
//public define Web_Action($State)
// http_action
// (
// String name,
// $State -> Bool allow,
// (List(Web_arg),$State) -> $State do_it
// ) =
// http_action(name,
// (Maybe($State) ms) |-> if ms is
// {
// failure then true,
// success(s) then allow(s)
// },
// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
// {
// failure then (failure, []),
// success(s2) then (success(do_it(l,s2)), [])
// }).
//
//public define Web_Action($State)
// https_action
// (
// String name,
// $State -> Bool allow,
// (List(Web_arg),$State) -> $State do_it
// ) =
// https_action(name,
// (Maybe($State) ms) |-> if ms is
// {
// failure then true,
// success(s) then allow(s)
// },
// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
// {
// failure then (failure, []),
// success(s2) then (success(do_it(l,s2)), [])
// }).
//
//public define Web_Action($State)
// http_https_action
// (
// String name,
// $State -> Bool allow,
// (List(Web_arg),$State) -> $State do_it
// ) =
// http_https_action(name,
// (Maybe($State) ms) |-> if ms is
// {
// failure then true,
// success(s) then allow(s)
// },
// (HTTP_Info h, List(Web_arg) l, Maybe($State) s) |-> if s is
// {
// failure then (failure, []),
// success(s2) then (success(do_it(l,s2)), [])
// }).
When you define your web site, you must provide the list of all the actions of the
site. When a new state has been computed, a graphical representation of this state
must be sent to the client. To that end, you must provide a function (named below
'compute_page') of type:
$State -> HTML_Page
where the type 'HTML_Page' (defined below in this file) abstractly represents HTML
pages.
public type HTML_Page:...
It should be clear that states and pages are deeply linked together. Indeed, we really
understand the page shown to the client as a representation of the current state of the
conversation between the client and the web site, but also containing informations
taken from the data bases.
** (1.5) States.
Now, we explain how you can define the type (say 'State') to be used as an instance of
the type parameter '$State'. The following is just a suggestion.
Each state determines a page (since 'compute_page' computes a page from a
state). However, some components of the state may be independant of the page. It may be
the case for example for the indication of the natural language used by the
client. Hence, a state should be made of (at least) two parts:
- informations which are the same for all pages,
- informations which are particular to each page.
For example, you could define:
type Page: // one alternative per page, with particular informations
login(...), // in the components
main_page(...),
...etc...
Now, the type 'State' could be defined as follows:
type State:
state(Language, // informations valid for all pages
...,
Page). // informations particular to a page
However, if you are making a secured web site within which clients should be identified
(by id and password), it may be a good idea to have two sorts of states, one for non
identified clients and one for identified clients. In this case, define the type
'State' as follows (this is just a suggestion):
type State:
non_identified(Language),
identified(String id,
Language,
Page).
When a request arrives, check if the previous state is 'identified(...)' or
'non_identified(...)', and don't provide access to certain pages to non identified
clients. This is required for security.
Some more words on security. If your site needs to identify clients, define the
initial state as 'non_identified(...)'. Construct a 'login' page, and check the id and
password of the client. If the id and password are correct, then change the state of
the client to 'identified(...)'. No other action should be able to do that. Now, be
confident that clients cannot forge states. The only information they have is the name
of a state, not the state itself which is never sent over the network, but only stored
on the server's disk. The name of the state is constructed using strong cryptographical
methods (sha1). If everything (since the 'login' page) is performed under HTTPS, even
state names cannot be seen by a third party. So, if the system retrieves a previous
state of the form 'identified(...)', you can be confident that your client is well
identified, and you can send him confidential informations.
States have a limited life time. It may happen that a client clicks on a button at a
time its state is out of date. In this case, this system considers that the new state
is a special state named 'ticket expired'. You must provide a function producing this
state when you describe your web site. The page corresponding to this state must just
inform the client that he/she waited a too long time before clicking on a button, and
has to restart (a new conversation) from the begining.
* (2) Carrying on.
** (2.1) Describing your web sites.
Before you may start your web site, you must describe it, i.e. produce a datum of the
opaque type 'Web_Site'.
public type Web_Site:...
Producing such a datum may be performed by:
public define Web_Site
make_web_site_description
(
List(String) common_names, // for example: ["www.our-business.com",
// "192.168.0.1"]
// the second one is just for testing
String site_directory, // where 'public' and other directories are
// located (should NOT end with '/')
One -> One init,
(HTTP_Info) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) initial_state,
($State expired,
HTTP_Info,
List(Web_arg),
Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_expired_state,
(HTTP_Info,
List(Web_arg),
Bool is_https) -> (Maybe($SessionTicket), Maybe($State), List(HTTP_header)) ticket_lost_state,
List(Web_Action($SessionTicket, $State)) actions,
(Maybe($SessionTicket), Maybe($State)) -> HTML_Page compute_page,
Int32 timeout, // seconds (todo: minutes)
List(Redirection) redirections,
String charset,
List(String) journal_extensions,
List(String) journal_headers,
String authorization_secret,
List(MIME) known_mime_types,
(String action_name,
List(Web_arg) args)-> One before_send_file
).
Explanations:
'common_names' is the list of names of the site (the name the browser must send as the
value of the 'Host' HTTP header in order to access the site must be in that list). Such
a name generally looks like this:
www.somewhere.com
If you are using HTTPS, you also have an 'X.509 SSL server certificate'. The name of
the site must be exactly the same as the name on the certificate (which is precisely
called the 'common name' in the X.509 jargon). If the two names do not match, the site
will still work, but the transaction will not be transparent to the client. His browser
will complain that the name of the certificate does not match the name of the site, and
he will have to accept the certificate manually.
'site_directory' is the absolute path to the directory where the files needed by the
site are located. Usually this directory looks like:
my_anubis/web_sites/www.somewhere.com
However, this information is not computed from 'common_name', so that you can change
the common name (for example temporarily, for networking reasons) without loosing
access to the files.
'ticket_expired_state(expired_state,http_info,lwa,is_https)' must produce the state
whose graphical representation is a page explaining to the user that its 'ticket' (or
'session information') has expired, and that he/she must close all popup windows and
start a new session. The arguments of the function contain the previous (expired)
state and all current informations concerning the user. This arguments may be useful
for example for producing the expiration message in the language chosen by the user.
You can also (and this may be much smarter) send a 'ticket prolongation page'
(including a new login for example), and resume the same conversation, since you have
all the pertinent informations at hand. In the case the ticket is definitely lost, the
second fonction 'ticket_lost_state' is used.
Notice that despite the fact that the parameter $State is involved in the arguments of
the above function, the type 'Web_Site' does not depend on this parameter. This allows
to produce lists of web site descriptions, where each description may be constructed
with a different instance of $State. This is required because distinct sites must have
distinct types of session informations. This is made possible by the fact that the
type is obscure, and the constructor replaced by a function which assembles
'ticket_expired_state', ticket_lost_state', 'actions' and 'compute_page' into a single
entity not depending on $State. You should have a look to the private part of this file
if you want more precisions about this programming technique.
'charset' is a string which will determine the character encoding to be used by the
browser. Typically, this string is one of: "UTF-8", "ISO-8859-1", "Windows-1252",
etc...
'before_send_file' is a function which is executed just before the HTTP server sends a
file. It gets an action name and the web arguments received with the request for that
file. Notice that this action name and these web arguments may be put into a
'private_download' element, and will come back to the server at the time of the
download.
** (2.2) Directories on the server's disk.
The description of you site contains the name of the directory within which the
required files are located. This may be for example:
my_anubis/web_sites/www.our-business.com/
This is called the 'site directory' (for the given site). Within the site directory,
the following directories are created by this program:
states
public
journal
private_download
upload_temporary
The directory 'states' is used for storing states (session informations). Out of date
states are automatically removed after some time.
The tree rooted at 'public' contains files that the server is allowed to send to the
clients. For security reasons, the server never sends a file which is not within the
tree whose root is this 'public' directory (except for the 'private download' mecanism;
see 'web/multihost_http_server.anubis'). Also, the MIME type (see 'web/mime.anubis')
must have been recognized before the file may be sent.
The directory 'journal' contains the jounal files. The roles of the remaining
directories 'private_download' and 'upload_temporary' is explained in
'multihost_http_server.anubis', where you will also find further informations on
'public' and 'journal'.
** (2.3) Web servers parameters.
The web servers have several parameters useful for administration. They are described
as follows:
public type WebServersParameters:
wsparms(Var(Bool) shutdown_required,
** (2.4) Starting your web sites.
When you have described all your web sites (you may want to have several web sites, and
they are distinguished by their 'common name'), you may start them all together using
'start_web_sites' below. This function returns a result of the following type:
public type Start_Web_Sites_Result:
cannot_bind_to_port(Int32),
cannot_bind_to_port(Int32,Int32),
ok(Server http_server,
Server https_server).
Indeed, it may happen that the system cannot bind (begin to listen) to one of the two
ports (or to both). The main reason is that another server is already listening on that
port. Another reason may be that 'anbexec' has not been correctly installed, i.e. that
the 's' bit has not been set for 'user' and 'group' (there is not such problem under
Windows). Also notice that the Linux kernel may need a rather long time (up to several
minutes) before liberating a listening port. Now, if the system can bind to the two
ports, the pair of the two servers is returned. Two tools are useful for manipulating
servers:
shutdown of type Server -> One
is_down of type Server -> Bool
They are defined in 'predefined.anubis' (together with the type 'Server').
public define Start_Web_Sites_Result
start_web_sites
(
Int32 ip_address, // the IP address shared by the web sites
Int32 http_port, // usually: 80
Int32 https_port, // usually: 443
String ssl_certificate_common_name,
List(Web_Site) web_sites, // web sites to be started
Var(Bool) shutdown_required
).
'ip_address' is the IP address on which the two servers listen. If you put 0, the
servers listen on all the IP addresses of the machine. This may be useful if the
machine has several network interfaces.
'ssl_certificate_common_name' is the common name of the SSL certificate that 'anbexec'
loads when it starts. One instance of 'anbexec' cannot handle more than one SSL server
certificate. This is due to a problem of conception of SSL itself. See the book 'SSL
and TLS' by Eric Rescorla (at Addison Wesley) for more explanations.
Notice that the number of servers is always 2, regardless of the number of web sites
you are starting.
The dynamic variable 'shutdown_required' may be used to control the shutdown of the two
servers from within the web site (typically the administration part). The servers will
shutdown as soon as this variable contains 'true'. So you must provide a variable
containing 'false' otherwise your servers will not run. You may also use the primitive
'must_restart' (see 'predefined.anubis') to control the restarting of your servers.
* (3) The HTML interface.
We propose an interface to dynamic HTML. Dynamic HTML includes HTML, and a combination
of CSS (Cascading Style Sheet) and JavaScript techniques for making HTML elements more
reactive and attractive on the client side.
** (3.1) Types used by the HTML interface.
For easy reference, we gather below the definitions of all the types used by the HTML
interface, and we comment them immediately.
public type HTML_Size:
absolute(Int32), // in pixels
percentage(Int32).
public type Text_Option:
size(Int32), // size of character font to use
font(String), // name of character font to use (such as "helvetica",...)
color(RGB), // color to be used for characters
italic,
oblique,
small_capitals,
bold,
underlined,
left_justified,
right_justified,
justified, // justified on both sides
line_through,
nowrap,
class(String), //CSS class
id(String),
// name(String),
tooltip(String).
A list of 'Text_Option' must be given with each text you want to put in your page.
This indicate the way of reading text.
public type Reading_Way:
ltr, //the text is readable from "Left To Right" like english
rtl. //the text is readable from "Right To Left" like arabic
// Following types are define to prohib any arguments order error
/**
* String value for 'id' attribut
*/
public type HtmlId:
htmlId(String id).
/**
* String value for 'class' attribut
*/
public type HtmlClass:
htmlClass(String class).
/**
* Name used in arguments list when form is submitted.
*/
public type WebArgName:
wan(String name).
/**
* Value passed to arguments list when form is submitted (for RadioButton and CheckBox).
*/
public type WebArgValue:
wav(String value).
/**
* Initial value of an input field.
*/
public type InitialValue:
init(String value).
public define Printable_tree [HtmlId x . Printable_tree y] = str_pt(x.id, y).
public define Printable_tree [HtmlClass x . Printable_tree y] = str_pt(x.class, y).
public define Printable_tree [WebArgName x . Printable_tree y] = str_pt(x.name, y).
public define Printable_tree [WebArgValue x . Printable_tree y] = str_pt(x.value, y).
public define Printable_tree [InitialValue x . Printable_tree y] = str_pt(x.value, y).
public type HtmlEvents:
// Form element events
onchange,
onsubmit,
onreset,
onselect,
onblur,
onfocus,
// Keyboard events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
onkeydown,
onkeypress,
onkeyup,
// Mouse events (Not valid in base, bdo, br, frame, frameset, head, html, iframe, meta, param, script, style, and title elements.)
onclick,
ondblclick,
onmousedown,
onmousemove,
onmouseout,
onmouseover,
onmouseup.
public type CoreAttrs:
id (String),
class (String),
style (String),
tooltip (String),
lang (String),
dir (Reading_Way),
accesskey (Word8),
tabindex (Int32),
attr (String, String),
event (HtmlEvents, String).
public type InputAttrs:
class (String),
style (String),
tooltip (String),
lang (String),
dir (Reading_Way),
accesskey (Word8),
tabindex (Int32),
attr (String, String),
event (HtmlEvents, String).
public type I18n:
lang (String),
dir (Reading_Way).
//public type DIV_Option:
// id (String),
// class (String),
// style (String),
// tooltip (String),
// lang (String),
// dir (Reading_Way),
// attr (String, String),
// event (HtmlEvents, String).
//
A list of 'DIV_Option' must be given with each DIV you want to put in your page.
define String event_name
(
HtmlEvents e
) =
if e is
{
// Form element events
onchange then "onchange",
onsubmit then "onsubmit",
onreset then "onreset",
onselect then "onselect",
onblur then "onblur",
onfocus then "onfocus",
// Keyboard events
onkeydown then "onkeydown",
onkeypress then "onkeypress",
onkeyup then "onkeyup",
// Mouse events
onclick then "onclick",
ondblclick then "ondblclick",
onmousedown then "onmousedown",
onmousemove then "onmousemove",
onmouseout then "onmouseout",
onmouseover then "onmouseover",
onmouseup then "onmouseup"
}.
public type Table_Option:
background_color(RGB), // applied to all cells in the table
background_image(String url),
border(Int32 width_of_outer_edge, // if not present, all values are 0
Int32 width_of_top_of_relief,
Int32 width_of_inner_edge,
RGB border_color),
width(Int32), // sets a minimal width for the table
percentage_width(Int32).
public define Table_Option nude = border(0,0,0,rgb(0,0,0)).
A list of 'Table_Option' must be given with each table.
public type BackgroundOption:
repeat, // repeat the background in both directions
repeat_horizontal, // repeat the background only horizontally
repeat_vertical, // repeat the background only verticall
no_repeat, // don't repeat the background
center.
public type Cell_Option:
core_attrs(List(CoreAttrs)),
left, // put the content of the cell on the left
h_center, // center the content of the cell horizontally
right, // put the content of the cell on the right
top, // put the content of the cell upwards
v_center, // center the content of tye cell vertically,
bottom, // put the content of the cell downwards
base_line, // align the content vertically according to base lines
background_color(RGB),
background_image(String url, BackgroundOption),
width(Int32), // sets a minimal width for the cell
percentage_width(Int32),
height(Int32), // sets a minimal height for the cell
columns(Int32), // lets the cell span over several columns
rows(Int32), // lets the cell span over several rows
nowrap. // do not allow text wrapping within the cell
A list of 'Cell_Option' must be given with each cell and each row in a table. Options
given with a row apply to all the cells in the row, but are superseded by options given
with cells, which apply only to the cell they are given with.
public type HTML_Cell($T):
cell(List(Cell_Option) options, $T content).
public type HTML_Header_Cell($T):
header_cell(List(Cell_Option) options, $T content).
The parameter $T is later instantiated either to 'HTML_In_Form' or to 'HTML_Off_Form',
depending on where you put your table (within a form or not within a form). For your
convenience, we define the following particular case:
public define HTML_Cell($T)
cell
(
$T content
) =
cell([],content).
public define HTML_Header_Cell($T)
header_cell
(
$T content
) =
header_cell([],content).
public type HTML_Row($T):
row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
public type HTML_Header_Row($T):
empty,
header_row(List(Cell_Option) options, List(HTML_Header_Cell($T)) cells).
public type HTML_Footer_Row($T):
empty,
footer_row(List(Cell_Option) options, List(HTML_Cell($T)) cells).
Same remark as for 'HTML_Cell($T)'. We define several convenience functions:
public define HTML_Row($T)
row
(
List(HTML_Cell($T)) cells
) =
row([],cells).
public define HTML_Row($T)
row
(
HTML_Cell($T) cell
) =
row([],[cell]).
public define HTML_Header_Row($T)
header_row
(
List(HTML_Header_Cell($T)) cells
) =
header_row([],cells).
public define HTML_Header_Row($T)
header_row
(
HTML_Header_Cell($T) cell
) =
header_row([],[cell]).
public type Actioner_Connection:
same, // use same type of connection as current page
http, // use non secured connection
https. // use secured connection
public type Other_Window_Option:
resizable, // the new window may be resized by the client
scrollbars, // the new window has scrollbars
width(Int32), // the new window has the specified width
height(Int32). // the new window has the specified height
public type Actioner_Target:
same,
same (String label),
other(String window_name, List(Other_Window_Option)).
public type Actioner_Aspect:
link (List(Text_Option), String url, Maybe(String) name),// hypertext link
img_link (String url, String img_url),
push_button (List(CoreAttrs),String text),
button (String url_off, String url_on), // rollover button
button (String url_off, String url_on, Int32 w, Int32 h), // idem with size
submit (List(CoreAttrs),String text),
immediate_selector (WebArgName name, Int32 size, List((WebArgValue,String)) choices),
immediate_selector (WebArgName name, Int32 size, List((WebArgValue,String)) choices, InitialValue selected).
public type Actioner_Local_Action:
close_window.
public define Actioner_Aspect
link
(
List(Text_Option) options,
String text
) =
link(options, text, failure).
public define Actioner_Aspect
link
(
String text
) =
link([], text, failure).
public define Actioner_Aspect
link
(
List(Text_Option) options,
Int32 i
) =
link(options,integer_to_string(i), failure).
public define Actioner_Aspect
link
(
Int32 i
) =
link([], i).
public define Actioner_Aspect
button
(
String url_img
) =
button(url_img,url_img).
Actioners are explained in details below.
public type TextAreaOption:
disabled,
read_only,
wrap_lines.
public type HTML_In_Form:
literal_pt (Printable_tree),
literal (String),
sequence (List(HTML_In_Form) items),
text (List(Text_Option), String the_text),
preformated (List(Text_Option), String),
paragraph (List(Text_Option), HTML_In_Form content),
image (String url),
image (String url, Int32 width, Int32 height),
table (List(Table_Option), HTML_Header_Row(HTML_In_Form), List(HTML_Row(HTML_In_Form)), HTML_Footer_Row(HTML_In_Form)),
center (HTML_In_Form),
mail_to (String email, HTML_In_Form element),
scroller (Int32 width, Int32 height,
Int32 content_width, Int32 content_height,
HTML_In_Form content),
actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
String action_name, List((String,String)) extra_ops,
List(Actioner_Local_Action)),
foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
foreign_link (List(Text_Option), String url),
foreign_link (List(Text_Option), String url, String name),
private_download (String abs_path, String name, String extra_ext,
Maybe((String,List((String,String)))) action),
text_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),
text_input_ro (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),
password_input (List(InputAttrs), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width),
text_area (List(TextAreaOption), String label_text, HtmlId id, WebArgName name, InitialValue init, Int32 width, Int32 height),
file_upload (String label, HtmlId id, WebArgName name, Int32 width),
selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List(String) choices),
selector (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List(String) choices, InitialValue selected),
// List((String,String)) = List((code,name)) where :
// code is the web-arg value
// name appears in selector
selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List((WebArgValue,String)) choices),
selector_c (List(InputAttrs), String label_text, HtmlId id, WebArgName name, Int32 size, List((WebArgValue,String)) choices, InitialValue selected),
radio_button (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
radio_button_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
check_box (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
check_box_r (List(InputAttrs), String label_text, HtmlId id, WebArgName name, WebArgValue value, Bool checked),
div (List(CoreAttrs), HTML_In_Form content),
div_empty (List(CoreAttrs)),
hidden (WebArgName name, InitialValue init).
'HTML_In_Form' defines all the elements you may put within a form. We define a
convenience function:
public define HTML_In_Form literal(Printable_tree t) = literal_pt(t).
public define HTML_In_Form
foreign_link
(
Int32 tsize,
String url,
String name
) =
foreign_link([size(tsize)],url,name).
public define HTML_In_Form
actioner
(
Actioner_Connection conn,
Actioner_Target targ,
Actioner_Aspect asp,
String action_name,
List((String,String)) extra_ops
) =
actioner(conn,targ,asp,action_name,extra_ops,[]).
public define HTML_In_Form
actioner
(
Actioner_Connection conn,
Actioner_Target targ,
Actioner_Aspect asp,
) =
actioner(conn, targ, asp, "", [], []).
public define HTML_In_Form
text_area
(
WebArgName name,
InitialValue init,
Int32 width,
Int32 height
) =
text_area([], "", htmlId(""), name,init,width,height).
public define HTML_In_Form
text_area
(
List(TextAreaOption) options,
WebArgName name,
InitialValue init,
Int32 width,
Int32 height
) =
text_area(options, "", htmlId(""), name,init,width,height).
public define HTML_In_Form
table
(
List(HTML_Row(HTML_In_Form)) rows
) =
table([],empty,rows,empty).
public define HTML_In_Form
table
(
List(Table_Option) options,
List(HTML_Row(HTML_In_Form)) rows
) =
table(options,empty,rows,empty).
public define HTML_In_Form
table
(
HTML_Header_Row(HTML_In_Form) h_row,
List(HTML_Row(HTML_In_Form)) rows
) =
table([],h_row,rows,empty).
public define HTML_In_Form
table
(
List(Table_Option) options,
HTML_Header_Row(HTML_In_Form) h_row,
List(HTML_Row(HTML_In_Form)) rows
) =
table(options,h_row,rows,empty).
public define HTML_In_Form
private_download
(
String abs_path,
String name,
String extra_ext
) =
private_download(abs_path,name,extra_ext,failure).
public define HTML_In_Form
private_download
(
String abs_path,
String name,
String extra_ext,
String action_name,
List((String,String)) args
) =
private_download(abs_path,name,extra_ext,success((action_name,args))).
public define HTML_In_Form
text
(
String s
) =
text([],s).
public define HTML_In_Form
radio_button (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
= radio_button (options, label_text, htmlId(n.name), n, value, checked).
public define HTML_In_Form
radio_button_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
= radio_button_r (options, label_text, htmlId(n.name), n, value, checked).
public define HTML_In_Form
check_box (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
= check_box (options, label_text, htmlId(n.name), n, value, checked).
public define HTML_In_Form
check_box_r (List(InputAttrs) options, String label_text, WebArgName n, WebArgValue value, Bool checked)
= check_box_r (options, label_text, htmlId(n.name), n, value, checked).
public define HTML_In_Form
file_upload (WebArgName name, Int32 width)
= file_upload("", htmlId(""), name, width).
public type HTML_Off_Form:
literal_pt (Printable_tree),
literal (String),
sequence (List(HTML_Off_Form) items),
text (List(Text_Option), String the_text),
preformated (List(Text_Option), String),
paragraph (List(Text_Option), HTML_Off_Form content),
image (String url),
image (String url, Int32 width, Int32 height),
table (List(Table_Option), HTML_Header_Row(HTML_Off_Form), List(HTML_Row(HTML_Off_Form)), HTML_Footer_Row(HTML_Off_Form)),
center (HTML_Off_Form),
mail_to (String email, HTML_Off_Form element),
scroller (Int32 width, Int32 height,
Int32 content_width, Int32 content_height,
HTML_Off_Form content),
fixed_size (HTML_Size width, HTML_Size height, HTML_Off_Form content),
fixed_size_2 (HTML_Size width, HTML_Size height, String name_of_HTML_file),
actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
String action_name, List((String,String)) extra_ops,
List(Actioner_Local_Action)),
actioner (Actioner_Connection, Actioner_Target, Actioner_Aspect,
String action_name, List((String,String)) extra_ops,
List(Actioner_Local_Action), String form_name),
foreign_link_new (Actioner_Target, Actioner_Aspect, String url),
foreign_link (List(Text_Option), String url),
foreign_link (List(Text_Option), String url, String name),
private_download (String abs_path, String name, String extra_ext,
Maybe((String,List((String,String)))) action),
label (String name),
form (String form_name, List(CoreAttrs), HTML_In_Form content),
form (String form_name, List(CoreAttrs),
String action_name, List((String,String)) extra_ops,
HTML_In_Form content),
div (List(CoreAttrs), HTML_Off_Form content),
div_empty (List(CoreAttrs)).
'HTML_Off_Form' defines all the elements you may put outside any form.
public define HTML_Off_Form literal(Printable_tree t) = literal_pt(t).
public define HTML_Off_Form fixed_size(HTML_Size width, HTML_Size height, String name_of_HTML_file)
= fixed_size_2(width,height,name_of_HTML_file).
public define HTML_Off_Form
foreign_link
(
Int32 tsize,
String url,
String name
) =
foreign_link([size(tsize)],url,name).
public define HTML_Off_Form
actioner
(
Actioner_Connection conn,
Actioner_Target targ,
Actioner_Aspect asp,
String action_name,
List((String,String)) extra_ops
) =
actioner(conn,targ,asp,action_name,extra_ops,[]).
public define HTML_Off_Form
table
(
List(HTML_Row(HTML_Off_Form)) rows
) =
table([],empty,rows,empty).
public define HTML_Off_Form
table
(
List(Table_Option) options,
List(HTML_Row(HTML_Off_Form)) rows
) =
table(options,empty,rows,empty).
public define HTML_Off_Form
table
(
HTML_Header_Row(HTML_Off_Form) h_row,
List(HTML_Row(HTML_Off_Form)) rows
) =
table([],h_row,rows,empty).
public define HTML_Off_Form
table
(
List(Table_Option) options,
HTML_Header_Row(HTML_Off_Form) h_row,
List(HTML_Row(HTML_Off_Form)) rows
) =
table(options,h_row,rows,empty).
We add two convenience functions for 'row'. The reason why we add two functions, one
for 'HTML_In_Form' and one for 'HTML_Off_Form', is that adding a schema with an
arbitrary '$T' creates too many ambiguities. This is due to the fact that, if we do so,
the arguments of the function do not refer to any of the types defined here.
public define HTML_Row(HTML_In_Form)
row
(
HTML_In_Form content
) =
row([],[cell([],content)]).
public define HTML_Row(HTML_Off_Form)
row
(
HTML_Off_Form content
) =
row([],[cell([],content)]).
public define HTML_Off_Form
private_download
(
String abs_path,
String name,
String extra_ext
) =
private_download(abs_path,name,extra_ext,failure).
public define HTML_Off_Form
private_download
(
String abs_path,
String name,
String extra_ext,
String action_name,
List((String,String)) args
) =
private_download(abs_path,name,extra_ext,success((action_name,args))).
public define HTML_Off_Form
text
(
List(Text_Option) lto,
Int32 i
) =
text(lto,integer_to_string(i)).
public define HTML_Off_Form
text
(
Int32 i
) =
text([],i).
public define HTML_Off_Form
text
(
String s
) =
text([],s).
- Cell a gap between two other cells :
public define HTML_Cell(HTML_Off_Form)
width_gap
(
Int32 w
) =
cell([width(w)],text([],"")).
public define HTML_Cell(HTML_In_Form)
width_gap
(
Int32 w
) =
cell([width(w)],text([],"")).
- Row a gap between two other rows :
public define HTML_Row(HTML_Off_Form)
height_gap
(
Int32 h
) =
row([],[cell([height(h)],text([],""))]).
public define HTML_Row(HTML_In_Form)
height_gap
(
Int32 h
) =
row([],[cell([height(h)],text([],""))]).
Notice that the two types have alternatives in common (same name, same arguments types,
up to the value of the parameter $T), which correspond to elements which may be put
anywhere in the page.
public type CSS_Style:
text_options(List(Text_Option)).
public type CSS_File:
css_file(String file_name).
public type JS_File:
js_file(String file_name).
define String
format
(
List(Text_Option) l
).
define Printable_tree
format_css_styles
(
List(CSS_Style) l
) =
if l is
{
[ ] then [ ],
[h . t] then
[ if h is
{
text_options(tos) then
[" body, span, p { ", format(tos), " }\n" ]
}
. format_css_styles(t)]
}.
public type HTML_Meta:
keywords (List(String)),
refresh (Actioner_Connection connection,
Actioner_Target target,
String action_name,
Int32 delay), // in seconds
refresh (String url, Int32 delay), // in seconds
meta (String name, String content),
http_equiv (String name, String content),
generic_meta (List((String,String))),
literal (String).
Meta tags are put in the 'head' of the HTML page.
public type Body_Option:
background_color (RGB),
background_image (String url),
background_image (String url, List(BackgroundOption)).
public type HTML_Body:
body(List(Body_Option) options, HTML_Off_Form content).
public type HTML_Page:
html_page(String title,
List(HTML_Meta) meta_tags,
List(CSS_Style) styles,
List(CSS_File) css_files,
List(JS_File) js_files,
HTML_Body body).
public define HTML_Page
html_page
(
String title,
List(HTML_Meta) metas,
HTML_Body body
) =
html_page(title,metas,[],[],[],body).
public define HTML_Page
html_page
(
String title,
List(HTML_Meta) metas,
List(CSS_Style) styles,
HTML_Body body
) =
html_page(title, metas, styles, [], [], body).
'HTML_Page' represents the final product of the construction of a web page.
*** (3.2) ``in form'' versus ``off form''.
There is a variety of HTML elements: texts, buttons, links, forms, inputs, etc... Some
of them may have a content, which is yet another HTML element (or several). Hence, it
is meaningful to say that an element is 'within' another one. Now, putting any element
within any other one may be meaningless. For example, an input element must be put
within a form (otherwise, it is useless), and a from within another form has no precise
meaning (and is forbidden by the HTML specification).
Actually, the main criterium is ``within a form or not within a form''. So, HTML
elements in a given page are separated into two categories: those who are within a
form, and the others. Nevertheless, there are elements which may belong to both
categories, like images and texts. We want to make use of the strong typing mecanism of
Anubis in order to forbid non meaningful placement of elements.
The type 'HTML_In_Form' defines elements to be put within forms. Similarly,
'HTML_Off_Form' defines elements not to be put within forms. Both types are recursive,
and 'HTML_Off_Form' refers to 'HTML_In_Form' (via the 'form' alternative, of course),
but the two types are not cross recursive. This is the reason why it is impossible to
put a form within a form. In order to construct a web page, you essentially have to
produce a datum of type 'HTML_Off_Form' (maybe containing data of type 'HTML_In_Form').
In practice, you don't have to worry so much about these two types, because elements
which may be put anywhere are constructed for both types by functions with the same
name and the same arguments. Hence, for both types, you just write the same thing. You
are warned by the compiler only when you try to put an element at a place it is not
allowed.
*** (3.3) Defining your own style.
We provide generic tools for constructing HTML elements. However, your web site needs
to have a ``style''.
To that end, you need to write down a set of ``styling functions'', using the tools
defined here. These styling functions allow the introduction of your colors and other
visual characteristics into the constructed elements once and for all. For example, you
may want all your texts to be rendered in the ``Helvetica'' font, in size 14 and using
some 'text_color'. You may write something like this:
define RGB text_color = rgb(10,40,40).
define HTML_Off_Form
text
(
String the_text
) =
text([font("helvetica"),size(14),color(text_color)],
the_text).
(and the same one for type 'HTML_In_Form') so that in order to put a piece of text in a
page, you just write:
text("... some text ...")
and you don't have to provide the font, size and color for each text. If you want to
have several styles of text presentation, you just write several sets of such
convenience functions. This also suggests a trick. You may want for example different
colors for 'in form' texts and 'off form' texts. This may be achieved automatically by
defining two functions as above, with the same name and same argument type, but
returning either a 'HTML_In_Form' or a 'HTML_Off_Form'.
If this preliminary work is well done, you will not waste your time later when you
concentrate on the actual informational content of your pages.
This general principle should be applied to all sorts of elements. This is the best
thing to do in order to separate the functions defining the visual style from the
functions defining the informational content itself, so that changing the style without
changing the content becomes easy. This is also the best way for having a clean and
easily readable source for your web site.
*** (3.4) Actioners and forms.
We have gathered several notions from HTML into that of an 'actioner'. An actioner is
an HTML element which opens a connection to our server when clicked upon. Actioners may
have different visual aspects. They may look like hypertext links or like buttons
(rollovers), or even like selectors (with immediate action). In any case, their
behavior is the same: they open a connection to our server, and send a set of 'web
arguments', i.e. pairs 'name=value'. Among these web arguments, one of them denotes
the action to be performed, and the others should be considered as operands for this
action. Actually, the precise behavior of the actioner has several variants.
The connection with the server may be secured (HTTPS) or non secured (HTTP). See the
type 'Actioner_Connection' above.
You must also choose where the answer must be rendered. This may be in the same window
or in another window (or frame). If it is in another window, the name of that window
must be given. If the window does not exist, the browser will create it. Optionally,
you may give the dimensions of the new window and other characteristics. See the type
'Actioner_Target' above.
The actioner also has a visual aspect. See the type 'Actioner_Aspect' above. In the
case of a rollover button, you provide the URLs of two images (of the same size)
representing the button:
url_off: to be used when the mouse is not over the button,
url_on: to be used when the mouse is over the button.
You can also create rollover buttons without creating images. Just use the second
alternative named 'button'. The server creates the images automatically.
The purpose of forms is just to give operands to actioners. If the actioner is placed
within a form, all the input elements which are within this form provide operands to
the actioner (except sometimes when they are not set by the client). If it is not put
within a form, the actioner gets no operand, except if the name of a form is explicitly
given, in which case the actioner gets all the inputs from that form as
operands. Furthermore, you may want to give extra operands to the actioner. This may be
useful for separating families of actioners with the same action name. Extra operands
'name=value' must be given in the form of pairs '(name,value)'.
Notice that the name of a form may be used by an actioner which is off the form, so as
to get the operands provided by this form. Also notice that several actioners may refer
to the same form, being either in the form, or referring to the form from the
outside. These actioners simply get the same set of operands, even if they correspond
to distinct actions.
Input elements may be put only within a form.
*** (3.5) Local popup.
This element looks like a link or a rollover button. When this button is clicked upon,
a 'popup window' appears. Actually, this popup window is just a layer in the same HTML
page, which becomes suddenly visible. It is realized with a '
' HTML tag. In
particular, clicking on the button does not open any connection. This is why it is
called 'local'. The arguments have the following roles:
Actioner_Aspect aspect of the button (same semantics as for actioners)
content content of the popup window
x, y, position of the popup window on the HTML page (not relative
to the button but to the page itself)
title title of the popup window
color color of the title bar and close button in the popup window.
A lightened version of this color is used for the background
of the popup window.
width width of the title bar
--- That's all for the public part ! --------------------------------------------------
----------------------------------- Table of Contents ---------------------------------
*** [1] States.
*** [1.1] Saving and retrieving states.
*** [1.2] Deleting out of date states.
*** [2] Tools.
*** [2.1] Directories.
*** [2.2] Secondary documents.
*** [3] Managing web arguments.
*** [3.1] Prefixing web arguments names.
*** [3.2] Separating web arguments.
*** [3.3] Applying an action.
*** [4] Web site descriptions and the 'awp handlers'.
*** [4.1] The type 'Web_Site'.
*** [4.2] Making a web site description.
*** [4.3] Starting the servers.
*** [5] HTML Formating.
*** [5.1] The type 'HTML_Any($T)'.
*** [5.2] Formating a color.
*** [5.3] Creating buttons.
*** [5.4] Formating an actioner.
*** [5.5] Formating a private download link.
*** [5.6] Formating rows and cells in a table.
*** [5.7] Formating elements which may be put anywhere.
*** [5.8] Formating 'in form' elements.
*** [5.9] Formating 'off form' elements.
*** [5.10] Formating meta-tags.
---------------------------------------------------------------------------------------
public define String
doctype_w3c_header
=
"\n".
*** [1] States.
We have to define functions for saving a state, retrieving a state, deleting out of
date states. We need one such function per web site. The types of the first two
functions depend on the parameter $State. This is not the case of the third one. The
fact that the instance of $State is variable from one web sites to the other implies
rather subtle manipulations using full functionality.
*** [1.1] Saving and retrieving states.
Each state is saved into a file on the server's disk (in the directory represented by
the symbol 'state_directory', which is 'my_anubis/web_sites/common_name/states'). The
state is saved together with a time stamp whose value is obtained by adding the current
time to the given timeout for states. The state receives a name obtained by hashing
(using sha1) the content of the file itself, and then encoding the hash with
'web_arg_encode'. The name of the file into which the state is saved is the
concatenation of "s" and the name of the state.
read CXM_web_arg_encode.anubis
The tool below constructs the function which is able to save a state on the server's
disk.
define (Maybe($State) s) -> String // the function constructed returns the name of the state
make_save_state_function
(
Int32 timeout,
String state_directory
) =
(Maybe($State) mbs) |->
if mbs is
{
failure then "",
success(s) then
with time_stamp = now+timeout,
to_be_saved = (time_stamp,s),
state_name = web_arg_encode(sha1(s)),
if save(to_be_saved,state_directory+"/s"+state_name) is ok
then state_name
else (print("Cannot create state file in '"+state_directory+"'.\n"); "")
}.
When a request arrives, we need to retrieve the previous state from the server's
disk. We receive the name of that state. If the state is out of date, the state file is
kept 3 days, and then deleted.
type PreviousState($State):
not_found, // cannot retrieve the previous state
out_of_date($State), // the previous state is out of date
still_valid($State). // the previous state is still valid
define (String state_name) -> PreviousState($State)
make_retrieve_state_function
(
String state_directory
) =
(String state_name) |->
with file_path = state_directory+"/s"+state_name,
if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
then (
if d is (time_stamp,s) then
if time_stamp < now
then (
forget(remove(file_path));
out_of_date(s)
)
else still_valid(s) // state has been successfully retrieved
)
else not_found.
*** [1.2] Deleting out of date states.
We also need to delete states which are out of date and which will never be deleted by
the above method. This may be performed by a machine doing this periodically (say once
per states life time period).
define (List(String) file_names) -> One
make_delete_out_of_date_states_function
(
Maybe($State) dummy,
String state_directory
) =
(List(String) file_names) |-df->
if file_names is
{
[ ] then unique,
[h . t] then
with file_path = state_directory+"/"+h,
if (RetrieveResult((Int32,$State)))retrieve(file_path) is ok(d)
then (
if d is (time_stamp,data) then
if time_stamp < now
then (forget(remove(file_path)); df(t))
else df(t)
)
else (forget(remove(file_path)); df(t))
}.
The 'labelled arrow' |-df-> is documented in 'documentation/en/anubis_doc.txt'.
Note: The argument 'dummy' (of type Maybe($State)) is not used in the body of the
function (hence its name). Nevertheless, it is required. Indeed, the Anubis compiler
does not accept a parameter in the body of a function (here the parameter is required
by the use of 'retrieve') if this parameter does not appear in the type of the
function. This is because this would create ambiguities that no explicit typing may
ever resolve. If you put a double slash in front of the declaration of 'dummy' above,
and if you compile this file, you will get a message like this one:
Error in 'making_a_web_site.anubis', line 1300, column 7:
A definition may not contain parameters which are not present
in the declaration part (hidden parameters):
$State
The type of the function constructed by 'make_delete_out_of_date_states_function' is
independant of the parameter $State. This is important because this allows to create
the list of such functions for all web sites. From this list, it is possible to call
the functions one after the other, so deleting out of date states for all web
sites. Actually, the next function receives a list of pairs (state_directory,function),
one for each web site.
define One
delete_out_of_date_states // for all web sites
(
List((String, List(String) -> One)) directories_and_functions
) =
if directories_and_functions is
{
[ ] then unique,
[h . t] then if h is (state_directory,function) then
function(directory_list(state_directory,"s*"));
delete_out_of_date_states(t)
}.
The above function must be called periodically in a separate virtual machine. The
period we have choosen is (rather logically) the life time of states itself. This may
be achieved by an 'infinite' loop, using a 'sleep(timeout)'. However, the loop must not
be really infinite, because the servers may be shutdown. Hence, our loop must test
(rather frequently; say every second) if the servers are down. If they are, the loop
must be exited.
define One
delete_states_loop
(
List((String,List(String) -> One)) directories_and_functions,
Int32 timeout,
Int32 next_time,
Server http_server,
Server https_server,
Var(Bool) shutdown_required
) =
if *shutdown_required
then (shutdown(http_server); shutdown(https_server))
else unique;
if (is_down(http_server) & is_down(https_server))
then unique
else if now > next_time
then
(
delete_out_of_date_states(directories_and_functions);
delete_states_loop(directories_and_functions,
timeout,
now+timeout,
http_server,
https_server,
shutdown_required)
)
else
(
sleep(1000); // sleep just one second and try again
delete_states_loop(directories_and_functions,
timeout,
next_time,
http_server,
https_server,
shutdown_required)
).
The above loop must be run in a separate virtual machine. This will be done just after
the two servers are started.
*** [2] Tools.
*** [2.1] Directories.
We need a tool for creating directories (if needed).
(This tool has been moved to 'tools/basis.anubis').
*** [2.2] Secondary documents.
Some HTML elements (like '