2009-05-13 17:46:00 +02:00
|
|
|
|
|
|
|
<html>
|
2009-05-14 15:55:28 +02:00
|
|
|
<style>
|
|
|
|
body {
|
|
|
|
background: #22252a;
|
|
|
|
color: #eee;
|
|
|
|
font-size: 16pt;
|
|
|
|
line-height: 150%;
|
|
|
|
font-family: times, Times New Roman, times-roman, georgia, serif;
|
|
|
|
}
|
|
|
|
#content {
|
|
|
|
max-width: 30em;
|
|
|
|
margin: 0 0 5em 10em;
|
|
|
|
}
|
|
|
|
#toc {
|
|
|
|
position: fixed;
|
|
|
|
top: 2em;
|
|
|
|
left: 0;
|
|
|
|
width: 10em;
|
|
|
|
}
|
|
|
|
#toc ol {
|
|
|
|
list-style: none;
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
padding-left: 1em;
|
|
|
|
}
|
|
|
|
#toc ol li {
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
}
|
|
|
|
#toc a { color: #777; }
|
|
|
|
|
2009-05-14 23:47:21 +02:00
|
|
|
h1, h2, h3, h4 { color: #bbb; }
|
2009-05-14 15:55:28 +02:00
|
|
|
|
|
|
|
h1 {
|
|
|
|
margin: 2em 0;
|
|
|
|
padding: 0px 0px 0px 0px;
|
|
|
|
font-size: 51px;
|
|
|
|
line-height: 44px;
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
h1 a { color: inherit; }
|
|
|
|
|
|
|
|
h2 {
|
2009-05-14 23:47:21 +02:00
|
|
|
font-size: 30px;
|
2009-05-14 15:55:28 +02:00
|
|
|
line-height: inherit;
|
|
|
|
font-weight: bold;
|
2009-05-14 23:47:21 +02:00
|
|
|
margin: 2em 0;
|
2009-05-14 15:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
h3 {
|
2009-05-14 23:47:21 +02:00
|
|
|
margin: 2em 0;
|
|
|
|
font-size: 20px;
|
|
|
|
line-height: inherit;
|
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
|
|
|
|
h4 {
|
2009-05-14 15:55:28 +02:00
|
|
|
margin: 1em 0;
|
2009-05-14 23:47:21 +02:00
|
|
|
font-size: inherit;
|
2009-05-14 15:55:28 +02:00
|
|
|
line-height: inherit;
|
2009-05-14 23:47:21 +02:00
|
|
|
font-weight: bold;
|
2009-05-14 15:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pre, code {
|
|
|
|
font-family: monospace;
|
2009-05-14 17:36:25 +02:00
|
|
|
font-size: 13pt;
|
2009-05-14 23:47:21 +02:00
|
|
|
color: #aaf;
|
2009-05-14 20:34:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pre {
|
|
|
|
padding-left: 2em;
|
2009-05-14 15:55:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
dl {
|
|
|
|
}
|
|
|
|
|
|
|
|
dt {
|
2009-05-14 20:34:14 +02:00
|
|
|
color: #aaf;
|
2009-05-14 15:55:28 +02:00
|
|
|
font-weight: bold;
|
|
|
|
}
|
|
|
|
|
|
|
|
dd {
|
|
|
|
margin: 1em 0;
|
|
|
|
margin-left: 1em;
|
|
|
|
}
|
|
|
|
|
|
|
|
a { color: #cd5; text-decoration: none; }
|
|
|
|
a:hover { text-decoration: underline; }
|
|
|
|
|
2009-05-14 20:34:14 +02:00
|
|
|
.highlight {
|
|
|
|
background: #733;
|
|
|
|
padding: 0.2em 0;
|
|
|
|
}
|
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
</style>
|
|
|
|
<title>node.js</title>
|
|
|
|
<body>
|
|
|
|
<div id="toc">
|
|
|
|
<ol>
|
|
|
|
<li><a href="#motivation">Motivation</a></li>
|
|
|
|
<li><a href="#benchmarks">Benchmarks</a></li>
|
|
|
|
<li><a href="#download">Download</a></li>
|
|
|
|
<li><a href="#install">Build</a></li>
|
|
|
|
<li><a href="#api">API</a>
|
2009-05-13 17:46:00 +02:00
|
|
|
<ol>
|
2009-05-14 15:55:28 +02:00
|
|
|
<li><a href="#timers">Timers</a>
|
|
|
|
<li><a href="#files">File System</a>
|
|
|
|
<li><a href="#tcp">TCP</a>
|
|
|
|
<li><a href="#http">HTTP</a>
|
2009-05-13 17:46:00 +02:00
|
|
|
<ol>
|
2009-05-14 15:55:28 +02:00
|
|
|
<li><a href="#http_server">Server</a>
|
|
|
|
<li><a href="#http_client">Client</a>
|
2009-05-13 17:46:00 +02:00
|
|
|
</ol>
|
2009-05-14 15:55:28 +02:00
|
|
|
<li><a href="#modules">Modules</a>
|
2009-05-13 17:46:00 +02:00
|
|
|
</ol>
|
2009-05-14 15:55:28 +02:00
|
|
|
</li>
|
|
|
|
</ol>
|
|
|
|
</div>
|
|
|
|
<div id="content">
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h1><a href="http://tinyclouds.org/node">Node</a></h1>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 20:34:14 +02:00
|
|
|
<p id="introduction"> Node is a purely asynchronous I/O framework for <a
|
|
|
|
href="http://code.google.com/p/v8/">V8 javascript</a>.
|
|
|
|
|
|
|
|
<p>This is an example of a web server written with Node which responds with
|
|
|
|
"Hello World" after waiting two seconds:
|
2009-05-13 21:37:01 +02:00
|
|
|
<pre class="sh_javascript">
|
2009-05-14 15:55:28 +02:00
|
|
|
new node.http.Server(function (msg) {
|
2009-05-13 17:46:00 +02:00
|
|
|
setTimeout(function () {
|
|
|
|
msg.sendHeader(200, [["Content-Type", "text/plain"]]);
|
|
|
|
msg.sendBody("Hello World");
|
|
|
|
msg.finish();
|
|
|
|
}, 2000);
|
|
|
|
}).listen(8000, "localhost");
|
|
|
|
</pre>
|
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p> This script can handle hundreds of concurrent requests while using
|
|
|
|
little CPU or memory—<a href="#benchmarks">see benchmarks</a>.
|
|
|
|
Check out <a href="#api">the documentation</a> for more examples.
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p> Node is free to <a href="#download">download</a>, <a
|
|
|
|
href="#api">use</a>, and <a href="#modules">build upon</a>.</p>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h2 id="motivation">Motivation</h2>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 23:47:21 +02:00
|
|
|
<h3>Evented Programming Makes More Sense</h3>
|
|
|
|
|
|
|
|
difference between blocking/non-blocking design
|
|
|
|
|
|
|
|
<p> There are many methods to write internet servers but they can
|
|
|
|
fundamentally be divided into two camps: evented and threaded; non-blocking
|
|
|
|
and blocking. A blocking server accepts a connection and launches a new
|
|
|
|
thread to handle the connection. Because the concurrency is handled by
|
|
|
|
the thread scheduler, a blocking server can make function calls which
|
|
|
|
preform full network requests.
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<pre class="sh_javascript">var response = db.execute("SELECT * FROM table");
|
|
|
|
// do something</pre>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 23:47:21 +02:00
|
|
|
<p> An evented server manages its concurrency itself. All connections
|
|
|
|
are handled in a single thread and callbacks are executed on certain
|
|
|
|
events: "socket 23 is has data to read", "socket 65's write buffer is
|
|
|
|
empty". An evented server executes small bits of code but never
|
|
|
|
<i>blocks</i> the process. In the evented world callbacks are used
|
|
|
|
instead of functions
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<pre class="sh_javascript">db.execute("SELECT * FROM table", function (response) {
|
2009-05-14 23:47:21 +02:00
|
|
|
// do something
|
2009-05-14 15:55:28 +02:00
|
|
|
});</pre>
|
2009-05-14 23:47:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
<p><a href="http://duartes.org/gustavo/blog/post/what-your-computer-does-while-you-wait">I/O latency</a>
|
2009-05-14 15:55:28 +02:00
|
|
|
<pre>
|
|
|
|
l1 cache ~ 3
|
|
|
|
l2 cache ~ 14
|
|
|
|
ram ~ 250
|
|
|
|
disk ~ 41000000
|
|
|
|
network ~ 240000000
|
|
|
|
</pre>
|
2009-05-14 23:47:21 +02:00
|
|
|
|
|
|
|
<p>purely evented interfaces rule out a lot of stupidity
|
|
|
|
|
|
|
|
<h3>Evented programs are more efficient</h3>
|
2009-05-14 15:55:28 +02:00
|
|
|
<ol>
|
|
|
|
<li>pthread stack size
|
|
|
|
2mb default stack size on linux (1mb on windows, 64kb on FreeBSD)
|
|
|
|
of course this is adjustable
|
|
|
|
<li>context switching benchmark
|
|
|
|
<li>Apache vs. Nginx
|
|
|
|
<li>event machine vs mongrel (neverblock)
|
|
|
|
</ol>
|
2009-05-14 23:47:21 +02:00
|
|
|
<h3>The appropriateness of Javascript</h3>
|
2009-05-14 15:55:28 +02:00
|
|
|
<ol>
|
|
|
|
<li>No I/O
|
2009-05-13 17:46:00 +02:00
|
|
|
<p> Javascript is without I/O. In the browser the DOM provides I/O,
|
|
|
|
but non-browser javascript interpreters have only non-standardized
|
|
|
|
functions to allow them print to console or access the network.
|
2009-05-14 15:55:28 +02:00
|
|
|
<li>No Threads
|
|
|
|
<li>Good compiler
|
|
|
|
<li>Universality of the language
|
2009-05-13 17:46:00 +02:00
|
|
|
<p> Contemporary computer infrastructure has two irreplaceable
|
|
|
|
languages: C and Javascript. C is the language of operating systems.
|
|
|
|
POSIX, the universal operating system API, is defined in C. So while you
|
|
|
|
can interface with operating systems in Java and Haskell, those
|
|
|
|
languages access must make system calls in C. Similarly, Javascript is
|
|
|
|
the language of the web operating system. In place of POSIX is the
|
|
|
|
DOM. You can wrap Javascript, you can compile to Javascript, but in the
|
|
|
|
end browsers must be interfaced with in Javascript. Portable low-level
|
|
|
|
systems tend to be written in C and portable web-level systems are
|
|
|
|
written in Javascript.
|
2009-05-14 15:55:28 +02:00
|
|
|
</ol>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h2 id="benchmarks">Benchmarks</h2>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p> TODO
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h2 id="download">Download</h2>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p> TODO
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h2 id="build">Build</h2>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<pre>configure
|
|
|
|
make
|
|
|
|
make install</pre>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h2 id="api">Application Programming Interface</h2>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p> The node executable should be given an argument pointing to a
|
|
|
|
javascript file.
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h3 id="timers">Timers</h3>
|
|
|
|
|
|
|
|
<p>The timer API is the same as in the browser. The functions
|
|
|
|
<code>setTimeout, setInterval, clearTimeout, and clearInterval</code>
|
|
|
|
|
|
|
|
<h3 id="files">File System</h3>
|
|
|
|
|
|
|
|
<h3 id="tcp">TCP</h3>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h3 id="http">HTTP (<code>node.http</code>)</h3>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p> Node provides a fast web server and client interface. The interface is
|
|
|
|
rather low-level. Node, for example, will not parse
|
|
|
|
<code>application/x-www-form-urlencoded</code> message bodies—that is
|
|
|
|
for higher level code to manage. The interface does abstract the
|
|
|
|
Transfer-Encoding (i.e. chuncked or identity), Message boundarys, and
|
|
|
|
Keep-Alive connections.
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h4 id="http_server">HTTP Server (<code>node.http.Server</code>)</h4>
|
|
|
|
<dl>
|
|
|
|
<dt><code>new Server(request_handler, options)</code></dt>
|
|
|
|
<dd>
|
|
|
|
<p>Creates a new web server. The <code>options</code> argument accepts
|
|
|
|
the same values as the options argument for
|
|
|
|
<code>node.tcp.Server</code> does. The options argument is optional.
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<p>The <code>request_handler</code> is a function which is called on
|
|
|
|
each request with a <code>Message</code> object argument.
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
<dt><code>server.listen(port, hostname)</code>
|
|
|
|
<dd>
|
|
|
|
<p>Begin accepting connections on the specified port and hostname. If the
|
|
|
|
hostname is omitted, the server will accept connections directed to any
|
|
|
|
address.
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
<dt><code>server.close()</code>
|
|
|
|
<dd>
|
|
|
|
<p>Stops the server. Requests currently in progress will not be
|
|
|
|
interrupted.
|
|
|
|
</dd>
|
|
|
|
</dl>
|
|
|
|
|
|
|
|
|
|
|
|
<h4>HTTP Request Message (<code>node.http.Message</code>)</h4>
|
|
|
|
|
|
|
|
<p> This object is only created internally—not by the user. It is passed
|
|
|
|
as an argument to the <code>request_handler</code> function in a web server.
|
|
|
|
<dl>
|
|
|
|
<dt><code>msg.method</code>
|
|
|
|
<dd>The request method as a string. Read only. Example: <code>"GET"</code>,
|
|
|
|
<code>"DELETE"</code>.</dd>
|
|
|
|
|
|
|
|
<dt><code>msg.uri</code>
|
|
|
|
<dd>The request URI as a string. Read only.
|
|
|
|
Example: <code>"/index.html?hello=world"</code>.</dd>
|
|
|
|
|
|
|
|
<dt><code>msg.headers</code>
|
|
|
|
<dd>The request headers expressed as an array of 2-element arrays. Read only.
|
|
|
|
Example:
|
|
|
|
<pre>
|
|
|
|
[ ["Content-Length", "123"]
|
|
|
|
, ["Content-Type", "text/plain"]
|
|
|
|
, ["Connection", "keep-alive"]
|
|
|
|
, ["Accept", "*/*"]
|
|
|
|
]
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
<dt><code>msg.http_version</code></dt>
|
|
|
|
<dd>The HTTP protocol version as a string. Read only. Examples: <code>"1.1"</code>,
|
|
|
|
<code>"1.0"</code>
|
|
|
|
|
|
|
|
<dt><code>msg.connection</code></dt>
|
|
|
|
<dd> A reference to the <code>node.tcp.Connection</code> object. Read
|
|
|
|
only. Note that multiple messages can be sent on a single connection.
|
|
|
|
</dd>
|
|
|
|
|
|
|
|
<dt><code>msg.onBody</code></dt>
|
|
|
|
<dd>Callback. Should be set by the user to be informed of when a piece
|
|
|
|
of the message body is received. Example:
|
|
|
|
<pre>
|
|
|
|
msg.onBody = function (chunk) {
|
|
|
|
puts("part of the body: " + chunk);
|
|
|
|
}
|
|
|
|
</pre>
|
2009-05-14 17:36:25 +02:00
|
|
|
A chunk of the body is given as the single argument. The transfer-encoding
|
2009-05-14 15:55:28 +02:00
|
|
|
has been removed.
|
2009-05-14 17:36:25 +02:00
|
|
|
<p>The body chunk is either a String in the case of utf8 encoding or an
|
|
|
|
array of numbers in the case of raw encoding.
|
2009-05-14 15:55:28 +02:00
|
|
|
|
|
|
|
<dt><code>msg.onBodyComplete</code></dt>
|
|
|
|
<dd>Callback. Made exactly once for each message. No arguments. After
|
|
|
|
<code>onBodyComplete</code> is executed <code>onBody</code> will no longer be called.
|
|
|
|
</dd>
|
|
|
|
|
2009-05-14 17:36:25 +02:00
|
|
|
<dt><code>msg.setBodyEncoding(encoding)</code></dt>
|
|
|
|
<dd>
|
|
|
|
Set the encoding for the request body. Either <code>"utf8"</code> or
|
|
|
|
<code>"raw"</code>. Defaults to raw.
|
|
|
|
<big>TODO</big>
|
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<dt><code>msg.sendHeader(status_code, headers)</code></dt>
|
|
|
|
<dd>
|
|
|
|
Sends a response header to the request. The status code is a 3-digit
|
|
|
|
HTTP status code, like <code>404</code>. The second argument,
|
|
|
|
<code>headers</code>, should be an array of 2-element arrays,
|
|
|
|
representing the response headers.
|
|
|
|
|
|
|
|
<p>Example:
|
|
|
|
<pre>
|
|
|
|
var body = "hello world";
|
|
|
|
msg.sendHeader( 200
|
|
|
|
, [ ["Content-Length", body.length]
|
|
|
|
, ["Content-Type", "text/plain"]
|
|
|
|
]
|
|
|
|
);
|
|
|
|
</pre>
|
|
|
|
This method must only be called once on a message and it must be called
|
|
|
|
before <code>msg.finish()</code> is called.
|
|
|
|
</dd>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<dt><code>msg.sendBody(chunk)</code></dt>
|
|
|
|
<dd>
|
|
|
|
This method must be called after <code>sendHeader</code> was called. It
|
|
|
|
sends a chunk of the response body. This method may be called multiple
|
|
|
|
times to provide successive parts of the body.
|
|
|
|
</dd>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<dt><code>msg.finish()</code></dt>
|
|
|
|
<dd>
|
|
|
|
This method signals that all of the response headers and body has been
|
|
|
|
sent; that server should consider this message complete.
|
|
|
|
The method, <code>msg.finish()</code>, MUST be called on each response.
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
</dl>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
<h3 id="modules">Modules</h3>
|
2009-05-13 17:46:00 +02:00
|
|
|
|
2009-05-14 20:34:14 +02:00
|
|
|
<p>Node has simple module loading. Here is an example. This is the file
|
|
|
|
<code>foo.js</code>:
|
2009-05-14 17:36:25 +02:00
|
|
|
<pre>
|
|
|
|
include("mjsunit");
|
|
|
|
|
|
|
|
function onLoad () {
|
|
|
|
assertEquals(1, 2);
|
|
|
|
}
|
|
|
|
</pre>
|
|
|
|
<p>Here the module <code>mjsunit</code> has provided the function
|
|
|
|
<code>assertEquals()</code>.
|
|
|
|
|
2009-05-14 20:34:14 +02:00
|
|
|
<p> The module file, <code>mjsunit.js</code>, must be in the same directory
|
|
|
|
as <code>foo.js</code> for <code>include()</code> to work. The
|
|
|
|
<code>include()</code> function will insert all the exported objects from the
|
|
|
|
module into the global namespace.
|
|
|
|
|
|
|
|
<p> Because file loading does not happen instantaneously, and because Node
|
|
|
|
has a policy of never blocking, the callback <code>onLoad()</code> is
|
|
|
|
provided to notify the user when all the exported functions are completely
|
|
|
|
loaded.
|
2009-05-14 17:36:25 +02:00
|
|
|
|
2009-05-14 20:34:14 +02:00
|
|
|
<p> To export an object, add to the special object <code class="highlight">exports</code>.
|
2009-05-14 17:36:25 +02:00
|
|
|
Let's look at how <code>mjsunit.js</code> does this
|
|
|
|
|
|
|
|
<pre>
|
|
|
|
function fail (expected, found, name_opt) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
|
|
|
function deepEquals (a, b) {
|
|
|
|
// ...
|
|
|
|
}
|
|
|
|
|
2009-05-14 20:34:14 +02:00
|
|
|
<span class="highlight">exports</span>.assertEquals = function (expected, found, name_opt) {
|
2009-05-14 17:36:25 +02:00
|
|
|
if (!deepEquals(found, expected)) {
|
|
|
|
fail(expected, found, name_opt);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
</pre>
|
|
|
|
<p> The functions <code>fail</code> and <code>deepEquals</code> are not
|
|
|
|
exported and remain private to the module.
|
|
|
|
|
|
|
|
<p> In addition to <code>include()</code> a module can use
|
|
|
|
<code>require()</code>. Instead of loading the exported objects into the
|
|
|
|
global namespace, it will return a namespace object. Again, the members of
|
2009-05-14 20:34:14 +02:00
|
|
|
the namespace object can only be guaranteed to exist after the
|
2009-05-14 17:36:25 +02:00
|
|
|
<code>onLoad()</code> callback is made. For example:
|
|
|
|
<pre>
|
|
|
|
var mjsunit = require("mjsunit");
|
|
|
|
|
|
|
|
function onLoad () {
|
|
|
|
mjsunit.assertEquals(1, 2);
|
|
|
|
}
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
|
2009-05-14 15:55:28 +02:00
|
|
|
</body>
|
2009-05-13 17:46:00 +02:00
|
|
|
</html>
|