package simple_httpd
Install
dune-project
Dependency
Authors
Maintainers
Sources
md5=92fe48592979d21002e66416c21bf398
sha512=88f090ce45b81cf244f248670608ff71c0db206da5de859c5e784e4fc59698acb329218bfffc81f4ff9796ae465c23e1faf08aaee804cb22dfe50cea757a854d
doc/vfs_pack.html
vfs_pack web site compiler and chaml preprocessor
vfs_pack
The binary vfs_pack, provided with Simple_httpd, will compile your website to a ML module of type Simple_httpd.Dir.VFS that can be used with Simple_httpd.Dir.add_vfs. It will
- compile a directory structure to declatation of several routes (see
Simple_httpd.Route), - it can be configured to allow directory listing or redirection to
index.html, - using the option
--max-size, large files are not stored into memory but may be ins talled in a separate directory, - precompressed version of a file (with deflate a.k.a zlib) are stored too (in memory or on disk).
- dynamic
.chamlcan be used to produce dynamic pages.
Here is an example of `.chaml` included in the distribution:
<!DOCTYPE html>
<html>
<head>
<?prelude
let count = try Request.get_cookie_int request "count"
with Not_found -> 0
let cookies = Cookies.(create ~max_age:3600L ~same_site:`Strict
~name:"count" (string_of_int (count+1)) empty)
?>
</head>
<body>
<h1>dynamic hello!</h1>
<ul>
<li> item 1 </li>
<li> item 2 </li>
<li> item 3 </li>
<li> item 4 </li>
<li> views: <?=string_of_int count?>
<?ml
let _ = echo (if count mod 2 = 0 then
{html|<p>even (<?= string_of_int count ?>)</p>|html}
else {html|<p>odd (<?= string_of_int count ?>)</p>|html})
?>
</li>
</ul>
<a href="."> escape from this file </a>
<br/>
request: <?ml
let _ = printf "%a" Request.pp request
?>
</body>
</html>As you can see, some defects of php are avoided: no quote everywhere, and some waranty about the generated html (see below).
Here is the documentation of the vfs_pack command line:
vfs-pack [opt]+Builds an OCaml module containing a Simple_httpd.Dir.Embedded_fs.t virtual file system. This is useful to pack assets into an OCaml binary, for example. Each entry in the VFS can be added from the command line.
-v verbose mode
-o <file> set output file
--file <name,file> adds name=file to the VFS
--url <name,url> adds name=url to the VFS
--mirror <prefix,dir> adds prefix=dir to the vfs, copying all files in directory dir
--max-size <size>, max size to hold file in memory (default: infinite). Bigger filed are copied to the folder given by --destination. A compressed version .zlib is also produced if it is at least 10% smaller.
--destination <dir> set the destination folder to use with mirror
--perm <int> set the permission of created folder
-F <file> reads entries from the file, written using this command line option syntax.
-help Display this list of options
--help Display this list of optionschaml
You may also build html contents inside OCaml code using our chaml preprocessor, to be used with the -pp option of ocaml compilers. chaml uses the same html parser as vfs_pack. Here is an example, part of the file examples/echo.ml from the distribution:
let _ =
Server.add_route_handler_chaml server ~filter Route.return
{chaml|
<!DOCTYPE html>
<html>
<head>
<title>index of echo</title>
</head>
<body>
<h3>welcome</h3>
<p><b>endpoints are</b></p>
<ul>
<li><pre>/ (GET)</pre> this file!
<li><pre>/hello/:name (GET)</pre> echo message
<li><pre><a href="/echo">echo</a></pre> echo back query
<li><pre>/upload/:path (PUT)</pre> to upload a file
<li><pre>/zcat/:path (GET)</pre> to download a file (deflate transfer-encoding)
<li><pre><a href="/stats/">/stats (GET)</a></pre> to access statistics
<li><pre><a href="/vfs/">/vfs (GET)</a></pre> to access a VFS
embedded in the binary
</ul>
</body>
</html>|chaml}The preprocessor can be used with such a dune rule:
(preprocess (per_module
((action (run chaml %{input-file})) ML_MODULES_WITH_CHAML_SYNTAX)))Syntax of chaml
There are four ways to insert ocaml code in html:
<?= expr ?>: that build html elements as a string from an ocaml expression. Like all other OCaml section, the valuerequest : stringSimple_httpd.Request.tis available.
- In tag attributes one may use
name=<?= expr?>to evaluate the attribute using Ocaml.
<?ml struct ?>: which should be an ocaml structure that define some values useful in the rest of the file or that uses a moduleOut : {!Html.Output}or a valueoutput = (module Out)to output contents. Unlike the previous case, no string are allocated and the content.
Values defined in such section are available in other sections at the same level of nesting (see below).
<?prelude struct ?>: similar to the previous one, but it is executed before any output is done to the server and may shadow two valuesheaders :Simple_httpd.Headers.tandcookies :Simple_httpd.Cookies.t, to set the headers and cookies of the response.
The response headers initially contains a Simple_httpd.Headers.header.Cache_Control with "no-store" value. You may add headers or change the Simple_httpd.Headers.header.Cache_Control, for instance if your chaml file is functional in the query, you could use Simple_httpd.Headers.header.ETag with some well chosen hash.
The latter response cookies are initially empty. For instance, this allows to read and modify cookies or start a Simple_httpd.Session.
Values defined in this section are part of a Prelude module which is accessible in <?=...?> and <?ml...?> sections.
Preferably (but this is not enforced by the parser), all prelude sections should at the beginning of the chaml code.
<?global struct ?>. These sections are placed in a module at the top level of the file generated by the commandvfs_packwhen it encounters a.chamlfile. (these sections are not permitted when using thechamlpreprocessor (see below)). The created module is named after the name of the chaml file. This means that achamlfile namedfoo.chamlin a foldermy_site, will result in a moduleMy_site.Foo.
Such a module is run only once at startup. This is useful for instance to create new keys using Simple_httpd.Session.new_key to associate data to a session. More generally, global sessions are used to prepare some types and data common to all requests.
Here is an example using sessions:
<!DOCTYPE html>
<?global let count_key : int Session.key = Session.new_key ()?>
<html>
<head>
<?prelude
let cookies, session = Session.start_check request
let count =
match Session.get_session_data session count_key with
| n -> n
| exception Not_found -> 0
let _ = Session.set_session_data session count_key (count+1)?>
</head>
<body>
...Next, you may insert html code inside ocaml code in three ways:
{html| html_contents |html}which produces a value of typestring
{funml| html_contents |funml}which produces a value of typeSimple_httpd.Html.elt= (module Html.Output) -> unit. If your ocaml code is itself insidehtml/chamlcode, a valueoutput = (module Out)is provided and can be use as argument for function of typeSimple_httpd.Html.elt.
{chaml| html_document |chaml}to be used with thechamlpreprocessor, but useless in.chamlfile. It produces a value of typeSimple_httpd.Html.chamlthat can be given to the functionSimple_httpd.Server.add_route_handler_chaml. This is useful to embed a html document inside an OCaml file.
Note about html syntax and the conformance of our parser
If you do not produce html tag yourself, all the html should be well parenthesised. By "writing yourself", we mean code like this:
"<div>" ^ some code producing a string ^ "</dvi>"which is considered bad (no verification are possible, and the typo will not be detected, probably meaning that your div will extend too far). You can not write code like:
{html|<div>|html} ^ some code producing a string ^ {html|</div>|html}it will give a parse error. Instead, you should write:
{html|<div><?= some code producing a string ?></div>|html}We tried to implement a superset of html 5 specifications as given in W3C html5 specification and the HTML Living Standard. More precisely, the code is generated by parsing these tables. The table misses a few tag names like <main> that we added manually. Please report missing tags or attributes.
In pure html, the nesting of html section is verified (rules like only <tr> can only appear in <tbody>, <thead> or <table>). However, rules about the order or unicity of html tags are not implemented (we do not check head before body). All end tags omissions permitted by the spectifications should be supported (like </li>). Attribute names are also checked according to the specification and foreign attributes are allowed everywhere. Attribute values are not checked. The special cases marked by asterisk sign in the above mentioned tables are not currently implemented.
No check is currently performed for MathMl or SVG. Arbitrary tags and attributes as well as CDATA are allowed inside <math>...</math> and <svg>...</svg>. Foreign tags and CDATA are not accepted anywhere else. There are a few problems with MathML or SVG, for instance self closing <script xlink:href="..."/> is valid in svg but not in html and is currently rejected.
We are planning to extend the parser to CSS, SVG and MathML in the future, which should solve such problems. Unfortunately, we did not find a "parsable" documentation for MathML or SVG to automatically generate the code.
The rule about unambiguous ampersand in html 5 is checked by recognizing entities (the code is generated from this json file. This means you do not have to encode the ampersand charater unless it looks like the starts of an html entities.
We checked our parser on the conformance-checkers tests at The web-platform-tests Project. The results are very good, near to 100% of valid files are accepted (invalid file are often accepted because we do not check the attributes value). In the html folder, two files are rejected, and it is not clear they obey the standards (for instance the <view-source> tag is defined nowhere and even its name is not valid in html 5). In the html-svg folder only very few valid files are rejected for reasons that should not occur in practice.