Lots of documentation improvements, especially in www.

This commit is contained in:
kristapsdz committed 2015-05-13 09:27:01 +00:00
1 parent 8d8ad02a75
commit 7408005f9f
14 files changed
+1240 -374

No files matched your search

+13 -4
View File
@@ -1,4 +1,4 @@
.SUFFIXES: .3 .3.html
.SUFFIXES: .3 .3.html .dot .svg .gnuplot .png
# Comment if you don't need statically linked.
# This is only for the sample program!
@@ -126,6 +126,9 @@ REGRESS_SRCS = regress/regress.c \
regress/test-file-get.c \
regress/test-ping.c \
regress/test-upload.c
SVGS = figure1.svg \
figure2.png \
figure3.png
all: libkcgi.a libkcgihtml.a libkcgijson.a libkcgixml.a libkcgiregress.a
@@ -212,11 +215,11 @@ install: all
sample: sample.o libkcgi.a libkcgihtml.a
$(CC) -o $@ $(STATIC) sample.o -L. libkcgihtml.a libkcgi.a -lz
www: index.html kcgi.tgz kcgi.tgz.sha512 $(HTMLS)
www: $(SVGS) index.html kcgi.tgz kcgi.tgz.sha512 $(HTMLS)
installwww: www
mkdir -p $(PREFIX)/snapshots
install -m 0444 index.html index.css $(HTMLS) $(PREFIX)
install -m 0444 index.html index.css $(SVGS) $(HTMLS) $(PREFIX)
install -m 0444 sample.c $(PREFIX)/sample.c.txt
install -m 0444 kcgi.tgz kcgi.tgz.sha512 $(PREFIX)/snapshots/
install -m 0444 kcgi.tgz $(PREFIX)/snapshots/kcgi-$(VERSION).tgz
@@ -245,8 +248,14 @@ kcgi.tgz:
(cd .dist && tar zcf ../$@ kcgi-$(VERSION))
rm -rf .dist
.dot.svg:
dot -Tsvg -o $@ $<
.gnuplot.png:
gnuplot $<
clean:
rm -f kcgi.tgz kcgi.tgz.sha512 index.html $(HTMLS) sample sample.o
rm -f kcgi.tgz kcgi.tgz.sha512 index.html $(SVGS) $(HTMLS) sample sample.o
rm -f libkcgi.a $(LIBOBJS)
rm -f libkcgihtml.a kcgihtml.o
rm -f libkcgijson.a kcgijson.o
+21
View File
@@ -0,0 +1,21 @@
digraph {
node [shape="ellipse"; width="0"; height="0"; margin="0.05,0.05"];
edge [arrowsize="0.5";];
subgraph cluster0 {
color="black";
client [label="application logic"];
subgraph cluster1 {
parser [shape="record"; label="{ HTTP parser | input validator }"];
color="red";
label="sandboxed child";
labelloc="b";
}
label="CGI client";
}
evil [label="CGI server"];
parser -> client [label="validated pairs"];
evil -> parser [label="CGI"];
client -> evil [label="HTTP"];
}
+27
View File
@@ -0,0 +1,27 @@
#!/usr/local/bin/gnuplot
set terminal pngcairo crop enhanced font "Times,15"
set output 'figure2.png'
set border linewidth 1.5
set style line 1 lc rgb '#800000' lt 1 lw 2
set style line 2 lc rgb '#ff0000' lt 1 lw 2
set style line 3 lc rgb '#ff4500' lt 1 lw 2
set style line 4 lc rgb '#ffa500' lt 1 lw 2
set style line 5 lc rgb '#006400' lt 1 lw 2
set style line 6 lc rgb '#0000ff' lt 1 lw 2
set style line 7 lc rgb '#9400d3' lt 1 lw 2
set style line 11 lc rgb '#808080' lt 1
set border 19 back ls 11
set tics nomirror out scale 0.75
set style line 12 lc rgb'#808080' lt 0 lw 1
set grid back ls 12
#########################################################
set xlabel 'milliseconds'
set size 0.8,0.8
set key outside right
set ytics 0.25
unset title
plot 'perf-time-openbsd-base.dat' u 2:($1/100) w l lw 3 ti '(1)', \
'perf-time-openbsd-base-cgi.dat' u 2:($1/100) w l lw 3 ti '(2)', \
'perf-time-openbsd-kcgi-nosandbox.dat' u 2:($1/100) w l lw 3 ti '(3)', \
'perf-time-openbsd-kcgi-nocompress.dat' u 2:($1/100) w l lw 3 ti '(4)', \
'perf-time-openbsd-kcgi.dat' u 2:($1/100) w l lw 3 ti '(5)'
+29
View File
@@ -0,0 +1,29 @@
#!/usr/local/bin/gnuplot
set terminal pngcairo crop enhanced font "Times,15"
set output 'figure3.png'
set border linewidth 1.5
set style line 1 lc rgb '#800000' lt 1 lw 2
set style line 2 lc rgb '#ff0000' lt 1 lw 2
set style line 3 lc rgb '#ff4500' lt 1 lw 2
set style line 4 lc rgb '#ffa500' lt 1 lw 2
set style line 5 lc rgb '#006400' lt 1 lw 2
set style line 6 lc rgb '#0000ff' lt 1 lw 2
set style line 7 lc rgb '#9400d3' lt 1 lw 2
set style line 11 lc rgb '#808080' lt 1
set border 19 back ls 11
set tics nomirror out scale 0.75
set style line 12 lc rgb'#808080' lt 0 lw 1
set grid back ls 12
#########################################################
set size 0.8,0.8
set ylabel "ms"
set xtics rotate by -45
set style fill solid border -1
set xtics ("static" 4, "slowcgi(8)" 3, "kcgi(3)" 2, "no sandbox" 1, "no compress" 0)
plot '-' w hist title ''
2.947
2.481
2.916
1.864
0.22
e
+81 -73
View File
@@ -1,77 +1,85 @@
html, body { margin: 0;
padding: 0; }
body { color: #333;
font-family: Times,serif;
font-size: 14pt;
line-height: 120%; }
code, pre { font-size: 11pt; }
a { text-decoration: none; }
header { box-shadow: 0 1px 4px #888; }
header nav ul { margin: auto 0;
padding: 0;
list-style-type: none; }
header nav li + li { margin-top: 0.5em; }
#header { background-color: #333;
padding: 3em 2% 1ex 2%;
color: #fff; }
#header a { color: #ddd; }
#header a:hover { color: #fff; }
#header nav { padding: 0.5ex 0 1ex 0; }
#version { background-color: #ddd;
padding: 1em 2%; }
html, body { margin: 0;
padding: 0; }
body { color: #333;
font-family: Times,serif;
font-size: 12pt;
line-height: 120%; }
code, pre { font-size: 11pt; }
a { text-decoration: none; }
header { box-shadow: 0 1px 4px #888; }
header nav ul { margin: auto 0;
padding: 0;
list-style-type: none; }
header nav li { margin: 0; }
header nav li + li { margin-top: 0.5em; }
#header { background-color: #333;
padding: 3em 5% 1ex 5%;
color: #fff; }
#header a { color: #ddd; }
#header a:hover { color: #fff; }
#header nav { padding: 0.5ex 0 1ex 0; }
#version { background-color: #ddd;
padding: 1em 5% 0 5%; }
#version a,
#version time,
#version span.version { font-style: normal; }
h1 { font-size: 32pt;
margin: 0 0 1ex 0;
font-weight: normal; }
h1 .nm { text-shadow: 1px 1px #666; }
h1 .nd { font-size: 28pt;
text-shadow: 1px 1px #333; }
footer { margin-top: 1em;
text-align: center;
color: #666; }
footer a { color: #000; }
article h2 { font-size: x-large;
font-weight: bolder;
color: #333; }
article h3 { font-size: large;
color: #333; }
figure { margin: 0;
color: #444; }
figcaption { padding: 0 1% 0.5ex 1%; }
figcaption span { font-weight: bolder; }
#intro { padding: 0 2%; }
#intro figure { width: 100%;
text-align: center; }
#intro img { max-width: 480px;
width: 100%; }
#sample { padding: 1ex 1%; }
#sample nav i { font-size: 16pt; }
#sample nav { padding: 0.5ex 1em; }
#sample nav a { color: #333; }
#sample nav a:hover i { text-shadow: 1px 1px #aaa; }
#comments { max-height: 0;
padding: 0; }
#nocomments { max-height: 100em;
padding: 1em; }
pre.prettyprint { border: none;
line-height: 150%;
margin: 0;
overflow: hidden;
border-radius: 1ex;
box-shadow: 1px 1px 2px #666;
width: auto; }
pre.prettyprint a { font-style: italic; }
pre.prettyprint a:hover { font-style: italic; }
pre.prettyprint ol.linenums > li
{ list-style-type: decimal; }
#features { padding: 1ex 2%; }
footer { padding: 1ex;
margin-top: 1em;
margin-bottom: 1em; }
#version span.version { font-style: normal; }
#version > a { font-size: 11pt;
color: #888;
display: block;
margin-top: 1ex;
text-shadow: 0 0 0px #000; }
#version > a:hover { color: #666; }
h1 { font-size: 32pt;
margin: 0 0 1ex 0;
font-weight: normal; }
.nm, .file { font-style: italic; }
h1 .nm { text-shadow: 1px 1px #666;
font-style: inherit; }
h1 .nd { font-size: 28pt;
text-shadow: 1px 1px #333; }
#intro { font-size: 14pt; }
footer { margin-top: 1em;
text-align: center;
color: #666; }
footer a { color: #000; }
article h2 { font-size: x-large;
font-weight: bolder;
color: #333; }
article h3 { font-size: large;
color: #333; }
article section,
article nav,
article figcaption { padding: 1ex 10%;
margin: 0; }
figure { margin: 0;
text-align: center;
color: #444; }
figcaption span { font-weight: bolder; }
dd { margin: 0 0 1em 0; }
.sample { text-align: left; }
.sample nav i { font-size: 16pt; }
.sample nav a { color: #333; }
.sample nav a:hover i { text-shadow: 1px 1px #aaa; }
nav[data-sblg-nav] { overflow: hidden; }
#longnews,
#comments { max-height: 0; }
#nolongnews,
#nocomments { max-height: 100em; }
pre.prettyprint { border: none;
line-height: 150%;
margin: 0;
overflow: hidden;
box-shadow: 1px 1px 2px #666;
width: auto; }
pre.prettyprint a { font-style: italic; }
pre.prettyprint a:hover { font-style: italic; }
#sample pre.prettyprint ol { padding: 0 10%; }
pre.prettyprint ol.linenums > li { list-style-type: decimal; }
footer { padding: 1ex;
margin-top: 1em;
margin-bottom: 1em; }
@media only screen and (max-width: 480px), only screen and (max-device-width: 480px) {
h1 { font-size: 16pt; }
h1 .nd { font-size: 14pt; }
pre { font-size: 8pt; }
h1 { font-size: 16pt; }
h1 .nd { font-size: 14pt; }
pre { font-size: 8pt; }
}
+278 -106
View File
@@ -8,25 +8,21 @@
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" />
<link rel="stylesheet" href="index.css" type="text/css" />
<script>
function showComments()
function showBits(name)
{
var show, nshow;
show = document.getElementById('comments');
noshow = document.getElementById('nocomments');
show = document.getElementById(name);
noshow = document.getElementById('no' + name);
console.log('maxheight = ' + show.style.maxHeight);
if (show.style.maxHeight == '' || show.style.maxHeight == '0px') {
show.style.maxHeight = '100em';
show.style.padding = '1em';
noshow.style.maxHeight = 0;
noshow.style.padding = 0;
} else {
show.style.maxHeight = 0;
show.style.padding = 0;
noshow.style.maxHeight = '100em';
noshow.style.padding = '1em';
}
}
</script>
@@ -46,9 +42,15 @@
</nav>
</section>
<section id="version">
<nav data-sblg-nav="1" data-sblg-navsz="3" data-sblg-navcontent="1">
Version <span class="version">${sblg-title}</span> (<time datetime="${sblg-date}">${sblg-date}</time>): ${sblg-aside}
<nav data-sblg-nav="1" data-sblg-navsz="1" data-sblg-navcontent="1" id="nolongnews">
<div>Version <span class="version">${sblg-title}</span> (<time datetime="${sblg-date}">${sblg-date}</time>)</div>
<div>${sblg-aside}</div>
</nav>
<nav data-sblg-nav="1" data-sblg-navcontent="1" id="longnews">
<div>Version <span class="version">${sblg-title}</span> (<time datetime="${sblg-date}">${sblg-date}</time>)</div>
<div>${sblg-aside}</div>
</nav>
<a href="#version" onclick="showBits('longnews');">Toggle All News</a>
</section>
</header>
<article>
@@ -57,19 +59,17 @@
<span class="nm">kcgi</span> is a minimal CGI library for web applications in <a
href="http://opensource.org/licenses/ISC" rel="license">ISC licensed</a> ISO C.
It was designed to be secure and auditable.
To start, download <a href="snapshots/kcgi.tgz">kcgi.tgz</a> and run <code>make install</code> into your
<code>PREFIX</code> of choice, then read <a href="kcgi.3.html">kcgi(3)</a>.
The system also ships with the <a href="kcgihtml.3.html">kcgihtml(3)</a>, <a
href="kcgijson.3.html">kcgijson(3)</a>, <a href="kcgixml.3.html">kcgixml(3)</a>, and <a
href="kcgi_regress.3.html">kcgi_regress(3)</a> libraries.
To start, <a href="#install">install</a> the library then read the <a href="#usage">usage guide</a>,
which links to the canonical manpage documentation.
Contact <a href="http://kristaps.bsd.lv">Kristaps</a> with questions or comments.
<span class="nm">kcgi</span> is a <a href="http://www.bsd.lv">BSD.lv</a> project.
</p>
</section>
<figure id="sample">
<figure id="sample" class="sample">
<figcaption>
<span>Figure</span>: this simple example implements a server that just echoes <q>Hello, World!</q> as an HTTP
The following simple example implements a server that just echoes <q>Hello, World!</q> as an HTTP
response.
Click on any italicitised fields to link to the documentation.
</figcaption>
<pre id="nocomments" class="prettyprint linenums">#include &lt;stdint.h&gt;
#include &lt;stdlib.h&gt;
@@ -129,36 +129,230 @@ int main(void) {
return(EXIT_SUCCESS);
}</pre>
<nav>
<a href="#" onclick="showComments(); return false;"><i class="fa fa-comments"></i> Toggle comments</a>
<a href="#sample" onclick="showBits('comments');"><i class="fa fa-comments"></i> Toggle comments</a>
</nav>
</figure>
<section id="features">
<section id="install">
<h2>
How does it work?
Installation
</h2>
<p>
The meat of <span class="nm">kcgi</span> lies in the <a href="khttp_parse.3.html">khttp_parse(3)</a>
function, which parses key-value input pairs from the HTTP request as well as determing the page
request itself, MIME type, and so on.
First, check if <span class="nm">kcgi</span> isn't already a third-part port for your system, such as
for <a href="http://cvsweb.openbsd.org/cgi-bin/cvsweb/ports/www/kcgi/">OpenBSD</a> or <a
href="https://svnweb.freebsd.org/ports/head/www/kcgi/">FreeBSD</a>.
If so, install using that system.
</p>
<p>
If not, you'll need a modern UNIX system.
To date, <span class="nm">kcgi</span> has been built and run on GNU/<a
href="http://www.linux.org">Linux</a> machines, BSD (<a
href="http://www.openbsd.org">OpenBSD</a>, <a href="http://www.freebsd.org">FreeBSD</a>), and <a
href="http://www.apple.com/osx">Mac OSX</a> (Snow Leopard, Lion) on i386 and AMD64.
It has been deployed under <a href="https://httpd.apache.org/">Apache</a>, <a
href="http://nginx.org">nginx</a>, and OpenBSD's <a
href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/httpd.8?query=httpd">httpd(8)</a>
(the latter two via the <code>slowcgi</code> wrapper).
Begin by downloading <a href="snapshots/kcgi.tgz">kcgi.tgz</a> and verify the archive with <a
href="snapshots/kcgi.tgz.sha512">kcgi.tgz.sha512</a>.
Once downloaded, compile the software with <code>make</code>, which will automatically run a
configuration script to conditionally deploy portability glue.
Finally, install the software using <code>make install</code>, optionally specifying the
<code>PREFIX</code> if you don't intend to use <span class="file">/usr/local</span>.
</p>
<p>
If <span class="nm">kcgi</span> doesn't compile, please send me the <span class="file">config.log</span>
file and the output of the failed compilation.
If you're running on an operating system with an unsupported sandbox, let me know and we can work
together to fit it into the configuration and portability layer.
</p>
</section>
<section id="usage">
<h2>
Usage
</h2>
<p>
While this section describes a fairly typical setup, you'll want to read <a
href="kcgi.3.html">kcgi(3)</a> and related for a fuller description.
The most relevant component of <span class="nm">kcgi</span> is input parsing, described in <a
href="khttp_parse.3.html">khttp_parse(3)</a>.
All key-value pairs are parsed from input, as are non-key-value message bodies.
Consider a sample application that wishes to process two named HTML fields, <code>string</code> (being a
non-empty string) and <code>integer</code>, a signed integer.
First, assign these fields to numeric identifiers.
This simplifies later access of the field values.
</p>
<figure class="sample">
<pre class="prettyprint linenums">enum key {
KEY_STRING,
KEY_INTEGER,
KEY__MAX
};</pre>
</figure>
<p>
Next, connect the indices with validation functions and names.
The validation function is run when the value is parsed; the name is the HTML form name for the given
element.
Validation applies to cookies as well as form and query-string data.
You can provide your own validation functions instead of using the stock <span class="nm">kcgi</span>
ones described in <a href="kvalid_string.3.html">kvalid_string(3)</a>, of course.
An empty string for the name will be applied to non-key-value message bodies.
</p>
<figure class="sample">
<pre class="prettyprint linenums">const struct kvalid keys[KEY__MAX] = {
{ kvalid_stringne, "string" },
{ kvalid_int, "integer" },
};</pre>
</figure>
<p>
Next, define a function that acts upon parsed fields.
Note that this is <q>application logic</q>, and thus constitutes only an example: each application will
handle its input differently.
For simplicity, I focus only on the <code>string</code> input.
If the value is found, it is assigned into the <code>fieldmap</code> array.
If it was found but did not validate, it is assigned into the <code>fieldnmap</code> array.
In this trivial example, the function emits the string values if found or indicates that they're not
found (or not valid).
There can be multiple inputs matching the same name, such as for HTML checkboxes.
</p>
<p>
Beyond directly writing to the HTTP document with <a href="khttp_write.3.html">khttp_write(3)</a> and
templating with <a href="khttp_template.3.html">khttp_template(3)</a>, <span class="nm">kcgi</span>
provides the <a href="kcgihtml.3.html">kcgihtml(3)</a>, <a href="kcgijson.3.html">kcgijson(3)</a>, and
<a href="kcgixml.3.html">kcgixml(3)</a> libraries for media-specific output functions.
These are intended only for the simplest use: complex applications will probably use their own.
</p>
<figure class="sample">
<pre class="prettyprint linenums">void process(struct kreq *r) {
struct kpair *p;
khttp_puts(r, "string = ");
if ((p = r->fieldmap[KEY_STRING]))
khttp_puts(r, p->parsed.s);
else if (r->fieldnmap[KEY_STRING])
khttp_puts(r, "failed parse");
else
khttp_puts(r, "not provided");
}</pre>
</figure>
<p>
Before doing any parsing, sanitise the HTTP context.
To begin, I provide an array of indexed page identifiers.
These define the page requests accepted by the application, in this case being only
<code>/index.html</code>.
</p>
<figure class="sample">
<pre class="prettyprint linenums">enum page {
PAGE_INDEX,
PAGE__MAX
};
const char *const pages[PAGE__MAX] = {
"index",
};</pre>
</figure>
<p>
Now, validate the page request and HTTP context based upon pre-parsed components.
This function checks the page request (it must be <code>/index</code>), MIME type (expanding to
<code>/index.html</code>), and HTTP method (it must be an HTTP <code>GET</code>, such as
<code>/index.html?string=foo</code>).
</p>
<figure class="sample">
<pre class="prettyprint linenums">int sanitise(struct kreq *r) {
if (PAGE_INDEX != r->page)
return(0);
else if (KMIME_TEXT_HTML != r->mime)
return(0);
return (KMETHOD_GET == r->method);
}</pre>
</figure>
<p>
Putting all of these together, parse the HTTP context, validate it, process it, then free the resources.
This simple example emits an HTTP 404 error regardless of the cause.
In reality, this will switch on HTTP 405 for unsupported methods and empty output for non-text media.
Headers are output using <a href="khttp_head.3.html">khttp_head(3)</a>, with the document body started
with <a href="khttp_body.3.html">khttp_body(3)</a>.
The HTTP context is closed with <a href="khttp_free.3.html">khttp_free(3)</a>.
</p>
<figure class="sample">
<pre class="prettyprint linenums">int main(void) {
struct kreq r;
if (KCGI_OK != khttp_parse(&amp;r, keys, KEY__MAX,
pages, PAGE__MAX, PAGE_INDEX))
return(EXIT_FAILURE);
if ( ! sanitise(&amp;r)) {
khttp_head(&amp;r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_404]);
khttp_head(&amp;r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[r.mime]);
khttp_body(&amp;r);
khttp_puts(&amp;r, "Page not found.");
} else {
khttp_head(&amp;r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&amp;r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[r.mime]);
khttp_body(&amp;r);
process(&amp;r);
}
khttp_free(&amp;r);
return(EXIT_SUCCESS);
};</pre>
</figure>
<p>
Usually, web applications for HTML content will use the page and input maps quite heavily, switching on
the page requested (which is usually known beforehand) and data sent along with the page.
CMS-style web applications will not parse the page identifier, leaving that to a database lookup.
Moreover, while HTML applications can generally disregard the HTTP method, a DAV implementation will
focus much more on the method itself.
</p>
</section>
<section id="features">
<h2>
Deploying
</h2>
<p>
Applications using <span class="nm">kcgi</span> behave just like any other application.
To compile <span class="nm">kcgi</span> applications, just include the <span class="nm">kcgi.h</span>
header file and make sure it appears in the compiler inclusion path.
(According to C99, you'll need to include <span class="nm">stdint.h</span> before it for the
<code>int64_t</code> type used for parsing integers.)
Linking is similarly normative: link to <span class="file">libkcgi</span> and, if your system has
compression support, <span class="file">libz</span>.
</p>
<p>
Well-deployed web servers, such as the default <a href="http://www.openbsd.org">OpenBSD</a> server, by
default are deployed within a <a
href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/chroot.2">chroot(2)</a>. If
this is the case, you'll need to statically link your binary.
If running within a <a
href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/chroot.2">chroot(2)</a> and on
OpenBSD, be aware that the sandbox method requires <span class="file">/dev/systrace</span> within the
server root.
By default, this file does not exist in the web server root.
Moreover, the default web server root mount-point, <span class="file">/var</span>, is mounted
<code>nodev</code>.
This complication does not exist for the other sandboxes.
</p>
</section>
<section id="features">
<h2>
Implementation Details
</h2>
<p>
The bulk of <span class="nm">kcgi</span> lies in <a href="khttp_parse.3.html">khttp_parse(3)</a>, which
fully parses the HTTP context.
Application developers must invoke this function before all others.
As mentioned in <a href="#security">security</a> section, the subroutines of
<a href="khttp_parse.3.html">khttp_parse(3)</a> are invoked inside of a sandboxed child process &#8211;
this isolates unvalidated, adversarial input.
It must be matched by an <a href="khttp_free.3.html">khttp_free(3)</a>.
It must be matched by an <a href="khttp_free.3.html">khttp_free(3)</a>, which frees all resources.
</p>
<p>
Once <a href="khttp_parse.3.html">khttp_parse(3)</a> has been successfully invoked, the calling
application is free to respond to the parsed and validated request.
</p>
<h3 id="security">
Security
</h3>
<p>
As a security precaution, the <span class="nm">kcgi</span> library parses and validates untrusted
network data in a sandboxed child process by forking within <a
href="khttp_parse.3.html">khttp_parse(3)</a>, where child process is responsible for reading and
parsing form data from the web server.
The <a href="khttp_parse.3.html">khttp_parse(3)</a> function isolates its parsing and validation of
untrusted network data within a <i>sandboxed</i> child process.
Sandboxes limit the environment available to a process, so exploitable errors in the parsing process (or
validation with third-party libraries) cannot touch the system environment.
This parsed data is returned to the parent process over a socket.
</p>
<figure>
<img src="figure1.svg" alt="Implementation Details" />
</figure>
<p>
This method of sandboxing the untrusted child process follows <a href="http://www.openssh.org">OpenSSH</a>,
and requires special handling for each operating system:
</p>
@@ -197,76 +391,11 @@ int main(void) {
abruptly killed by the sandbox facility.
If required, this kind of validation can take place after the parse validation sequence.
</p>
<h3 id="portability">
Portability
</h3>
<p>
<span class="nm">kcgi</span> should run on any modern UNIX systems and with any web server.
To date, it has been built and run on GNU/<a href="http://www.linux.org">Linux</a> machines, BSD (<a
href="http://www.openbsd.org">OpenBSD</a>, <a href="http://www.freebsd.org">FreeBSD</a>), and <a href="http://www.apple.com/osx">Mac OSX</a>
(Snow Leopard, Lion) on i386 and AMD64.
It has been deployed under <a href="https://httpd.apache.org/">Apache</a>, <a
href="http://nginx.org">nginx</a>, and OpenBSD's <a
href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/httpd.8?query=httpd">httpd(8)</a>
(the latter two via the <code>slowcgi</code> wrapper).
</p>
<p>
Portability across UNIX systems is made possible by a small <code>configure</code> script that checks
for minor inconsistencies such as <code>strlcpy(3)</code>, the <a href="#security">Security</a> mechanisms,
and for <a href="#compression">Compression</a> support.
</p>
<h3 id="extending">
Extensibility
</h3>
<p>
While page maps and input validation are entirely driven by the interfacing application, <span
class="nm">kcgi</span> also allows for extension of the default HTTP headers, schemas, MIME
types, and so on.
Reasonable default have been provided for convenience.
For specifics, see <a href="khttp_parse.3.html">khttp_parse(3)</a>.
</p>
<p>
The library can also be extended for different output modes.
Two such modes, <a href="kcgihtml.3.html">kcgihtml(3)</a> and <a href="kcgijson.3.html">kcgijson(3)</a>,
are bundled with the system.
It allows a mechanism for building HTML5 trees around the usual
<a href="khttp_write.3.html">khttp_write(3)</a> family of functions.
</p>
<h3 id="compression">
Compression
</h3>
<p>
If <code>HAVE_ZLIB</code> is enabled during compilation (via the <a href="#portability">Portability</a>
mechanism), <a href="khttp_body.3.html">khttp_body(3)</a> will signal use of <a
href="http://www.zlib.net">zlib</a> to compress the HTTP body.
Compression is only enabled if the client provides the correct (<q>gzip</q>) HTTP request header.
</p>
<h3 id="input">
Input Processing
</h3>
<p>
All common input methods&#8212;query string, cookie, and form (multipart form-data and mixed,
urlencoded, and plain&#8212;are supported by <span class="nm">kcgi</span>.
As described in the <a href="#security">Security</a> section, these fields are all parsed and validated
from network data in a child process.
Each input key-value pair can be matched (by key name) to a validator, which is run when fields are
parsed.
You can then look up key-value pairs constant-time in a table indexed by that key.
</p>
<h3 id="template">
Templating
</h3>
<p>
Many application will want just to fill in an output template instead of creating complex output trees
(relegating most work to JavaScript and JSON).
<span class="nm">kcgi</span> provides the <a href="khttp_template.3.html">khttp_template(3)</a> family
of functions to fill in files or memory buffers with data.
Templates are the most common usage of <span class="nm">kcgi</span>, as they allow for a strong
disconnect between prsentation and logic.
</p>
<h3 id="testing">
</section>
<section>
<h2 id="testing">
Testing
</h3>
</h2>
<p>
<span class="nm">kcgi</span> is shipped with a fully automated testing framework executed with
<code>make regress</code>.
@@ -292,6 +421,49 @@ int main(void) {
<code>afl-urlencoded</code> binaries using the test cases (and dictionaries, for the first) provided.
</p>
</section>
<section>
<h2>
Performance
</h2>
<p>
Security comes at a price.
By design, <span class="nm">kcgi</span> incurs overhead in three ways: first, spawning a child to
process the untrusted network data; second, enacting the sandbox framework; and third, passing parsed
pairs back to the parent context.
</p>
<figure>
<img src="figure2.png" />
</figure>
<p>
This figure illustrates the cost of running <span class="nm">kcgi</span> against a baseline on OpenBSD
5.5 running <a href="http://nginx.org">nginx</a> and <a
href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/slowcgi.8">slowcgi(8)</a>, i386.
It shows the empirical cumulative distribution of a statisically-significant number of page requests
(&gt;1000) as measured by <a href="https://httpd.apache.org/docs/2.2/programs/ab.html">ab(1)</a>.
</p>
<p>
Line (1) shows a static file being served by the web server.
The high speed is due to the file (most likely) being cached by the web server and/or kernel.
Moving right, line (2) shows a basic CGI request producing no content.
The CGI simply exits with an HTTP 200.
This reflects the cost of invoking <a
href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man8/slowcgi.8">slowcgi(8)</a>.
Line (3) is a simple <span class="nm">kcgi</span> that simply emits an HTTP 200.
This instance is neither sandboxed nor does it compress output.
The overhead from line (2) is due to the additional child being spawned.
Line (4) shows the addition of sandboxing without compression, in this case via
<a href="http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man4/systrace.4">systrace(4)</a>.
Lastly, line (5) shows both compression and sandboxing.
</p>
<figure>
<img src="figure3.png" />
</figure>
<p>
In this bar chart, I show the same but for relative comparison of the distribution means.
The similarity between the compressed and non-compressed versions are due to the small amount of data
being transmitted in the response body.
</p>
</section>
</article>
<footer>
<div>&#169; 2014 <a rel="author" href="http://kristaps.bsd.lv">Kristaps Dzonsons</a></div>
+67
View File
@@ -95,6 +95,73 @@ library, link as follows:
.Bd -literal
% cc -o sample -static sample.o -lkcgihtml -lkcgi -lz
.Ed
.Sh EXAMPLES
The following simple example assumes that
.Pa kcgi.h
is in the compiler's include path.
It does nothing but emit
.Dq Hello, world!
to the output.
.Bd -literal
#include <stdint.h>
#include <stdlib.h>
#include <kcgi.h>
int main(void) {
struct kreq r;
const char *page = "index";
if (KCGI_OK != khttp_parse(&r, NULL, 0, &page, 1, 0))
return(EXIT_FAILURE);
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[r.mime]);
khttp_body(&r);
khttp_puts(&r, "Hello, world!");
khttp_free(&r);
return(EXIT_SUCCESS);
}
.Ed
.Pp
In a more complicated example,
.Nm
accepts a single parameter
.Dq string
to validate and display.
This might be provided as part of an HTML form or directly as part of
the URL query string.
.Bd -literal
#include <stdint.h>
#include <stdlib.h>
#include <kcgi.h>
int main(void) {
struct kreq r;
struct kpair *p;
const char *page = "index";
struct kvalid key = { kvalid_stringne, "string" };
if (KCGI_OK != khttp_parse(&r, &key, 1, &page, 1, 0))
return(EXIT_FAILURE);
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[r.mime]);
khttp_body(&r);
khttp_puts(&r, "Result: ");
if ((p = r.fieldmap[0]))
khttp_puts(&r, p->parsed.s);
else if (r.fieldnmap[0])
khttp_puts(&r, "bad parse");
else
khttp_puts(&r, "no value");
khttp_free(&r);
return(EXIT_SUCCESS);
}
.Ed
.Pp
Applications will usually specify an array of key-value pairs to
validate; or in the event of web services, a default validator (empty
string) for the full HTTP message body.
.Sh SEE ALSO
.Xr kcgi 3 ,
.Xr kcgihtml 3 ,
+218 -190
View File
@@ -56,49 +56,13 @@ and
.Nm khttp_parsex
functions parse and validate input and the HTTP environment
(compression, paths, MIME types, and so on).
It parses and validates data as key-value pairs for the
.Li application/x-www-form-urlencoded ,
.Li multipart/form-data ,
and
.Li text/plain
.Pq if and only if the method is POST
content types, and opaque message bodies (defaulting to
.Li application/octet-stream )
otherwise.
Key-value pairs are also parsed from cookies and the query string.
It is the central function in the
.Xr kcgi 3
library, parsing and validating key-value form (query string, message
body, cookie) data and opaque message bodies.
.Pp
The collective arguments are as follows:
.Bl -tag -width Ds
.It Fa req
Fill with input fields and HTTP context parsed from the CGI environment.
This is the main structure carried around in a
.Xr kcgi 3
application.
.It Fa keys
An array of input and validation fields.
.It Fa keysz
The number of elements in
.Fa keys .
.It Fa pages
An array of recognised pathnames.
When pathnames are parsed, they're matched to indices in this array.
.It Fa pagesz
The number of pages in
.Fa pages .
Also used if the requested page was not in
.Fa pages .
.It Fa defmime
If no MIME type is specified (that is, there's no suffix to the
page request), use this index in the
.Fa mimes
array.
.It Fa defpage
If no page was specified (e.g., the default landing page), this is
provided as the requested page index.
.It Fa suffixes
Define the MIME type (suffix) mapping.
.It Fa maxmime
The MIME index used if no MIME type was matched.
.It Fa arg
A pointer to private application data.
It is not touched unless
@@ -109,14 +73,48 @@ Function invoked with
.Fa arg
by the child process starting to parse untrusted network data. This
makes sure that no unnecessary data is leaked into the child.
.It Fa defmime
If no MIME type is specified (that is, there's no suffix to the
page request), use this index in the
.Fa mimes
array.
.It Fa defpage
If no page was specified (e.g., the default landing page), this is
provided as the requested page index.
.It Fa keys
An array of input and validation fields.
.It Fa keysz
The number of elements in
.Fa keys .
.It Fa maxmime
The MIME index used if no MIME type was matched.
.It Fa pages
An array of recognised pathnames.
When pathnames are parsed, they're matched to indices in this array.
.It Fa pagesz
The number of pages in
.Fa pages .
Also used if the requested page was not in
.Fa pages .
.It Fa req
Fill with input fields and HTTP context parsed from the CGI environment.
This is the main structure carried around in a
.Xr kcgi 3
application.
.It Fa suffixes
Define the MIME type (suffix) mapping.
.El
.Pp
The first form is for applications using the system-recognised MIME
types, which should work well enough for most applications.
It is equivalent to the following:
The first form,
.Nm khttp_parse ,
is for applications using the system-recognised MIME types.
This should work well enough for most applications.
It is equivalent to invoking the second form,
.Nm khttp_parsex ,
as follows:
.Bd -literal -offset indent
khttp_parsex(req, ksuffixmap, KIME__MAX,
keys, keysz, pages, pagesz, NULL, NULL);
keys, keysz, pages, pagesz, NULL, NULL);
.Ed
.Pp
The
@@ -140,6 +138,76 @@ It consists of the following fields:
Private application data.
This is set during
.Fn khttp_parse .
.It Va auth
Type of
.Qq managed
HTTP authorisation, if any.
This is digest
.Pq Dv KAUTH_DIGEST
or basic
.Pq Dv KAUTH_BASIC
authorisation performed by the web server.
See the
.Va rawauth
field for raw authorisation requests.
If a managed authorisation is specified but with unknown type (i.e., not
digest or basic authentiation), this is set to
.Dv KAUTH_UNKNOWN .
.It Va cookies
All key-value pairs read from user cookies.
.It Va cookiemap
Entries in successfully-parsed (or un-parsed)
.Va cookies
mapped into field indices as defined by the
.Fa keys
argument to
.Fn khttp_parse .
.It Va cookienmap
Entries in unsuccessfully-parsed (but still attempted)
.Fa cookies
mapped into field indices as defined by the
.Fa keys
argument to
.Fn khttp_parse .
.It Va cookiesz
The size of the
.Va cookies
array.
.It Va fields
All key-value pairs read from the requests (query string, cookies,
message body).
.It Va fieldmap
Entries in successfully-parsed (or un-parsed)
.Fa fields
mapped into field indices as defined by the
.Fa keys
arguments to
.Fn khttp_parse .
.It Va fieldnmap
Entries in unsuccessfully-parsed (but still attempted)
.Fa fields
mapped into field indices as defined by the
.Fa keys
argument to
.Fn khttp_parse .
.It Va fieldsz
The number of elements in the
.Va fields
array.
.It Va fullpath
The full path following the server name or
.Dv NULL
if there is no path following the server.
For example, if
.Pa foo.cgi/bar/baz
is the
.Dv PATH_INFO ,
this would be
.Pa /bar/baz .
.It Va host
The host-name (i.e., the host of the web application) request passed to
the application.
This shouldn't be confused with the application host's canonical name.
.It Va method
The
.Dv KMETHOD_ACL ,
@@ -176,67 +244,14 @@ and
so be sure to emit a
.Dv KHTTP_405
status for non-conforming methods.
.It Va auth
Type of
.Qq managed
HTTP authorisation, if any.
This is digest
.Pq Dv KAUTH_DIGEST
or basic
.Pq Dv KAUTH_BASIC
authorisation performed by the web server.
See the
.Va rawauth
field for raw authorisation requests.
If a managed authorisation is specified but with unknown type (i.e., not
digest or basic authentiation), this is set to
.Dv KAUTH_UNKNOWN .
.It Va rawauth
If the web server passes the
.Qq Authorization
header (which, for example, Apache doesn't by default), then the header
is parsed into this field, which is of type
.Vt struct khttpauth .
.It Va cookies
All key-value pairs read from user cookies.
.It Va cookiesz
The size of the
.Va cookies
array.
.It Va cookiemap
Entries in successfully-parsed (or un-parsed)
.Va cookies
mapped into field indices as defined by the
.Fa keys
argument to
.It Va kdata
Internal data.
Should not be touched.
.It Va keys
Value passed to
.Fn khttp_parse .
.It Va cookienmap
Entries in unsuccessfully-parsed (but still attempted)
.Fa cookies
mapped into field indices as defined by the
.Fa keys
argument to
.Fn khttp_parse .
.It Va fields
All key-value pairs read from the requests (query string, cookies,
message body).
.It Va fieldsz
The number of elements in the
.Va fields
array.
.It Va fieldmap
Entries in successfully-parsed (or un-parsed)
.Fa fields
mapped into field indices as defined by the
.Fa keys
arguments to
.Fn khttp_parse .
.It Va fieldnmap
Entries in unsuccessfully-parsed (but still attempted)
.Fa fields
mapped into field indices as defined by the
.Fa keys
argument to
.It Va keysz
Value passed to
.Fn khttp_parse .
.It Va mime
The MIME type of the requested file as determined by its
@@ -275,6 +290,8 @@ if the path failed lookup.
.It Va pagename
The string corresponding to
.Va page .
.It Va port
The server's receiving TCP port.
.It Va path
The path (or empty string) following the parsed component regardless of
whether it was located in the path array provided to
@@ -286,6 +303,27 @@ is
the path component would be
.Pa baz
.Pq with the leading slash stripped .
.It Va pname
The script name (which may be an empty string in degenerate cases)
passed to the server.
This may not reflect a file-system entity if re-written by the web
server.
.It Va rawauth
If the web server passes the
.Qq Authorization
header (which, for example, Apache doesn't by default), then the header
is parsed into this field, which is of type
.Vt struct khttpauth .
.It Va remote
The string form of the client's IPV4 or IVP6 address.
.It Va scheme
The access scheme, which is either
.Dv KSCHEME_HTTP
or
.Dv KSCHEME_HTTPS .
The scheme defaults to
.Dv KSCHEME_HTTP
if not specified by the request.
.It Va suffix
The suffix part of the
.Dv PATH_INFO
@@ -301,53 +339,39 @@ the suffix would be
See the
.Va mime
field for the MIME type parsed from the suffix.
.It Va fullpath
The full path following the server name or
.Dv NULL
if there is no path following the server.
For example, if
.Pa foo.cgi/bar/baz
is the
.Dv PATH_INFO ,
this would be
.Pa /bar/baz .
.It Va scheme
The access scheme, which is either
.Dv KSCHEME_HTTP
or
.Dv KSCHEME_HTTPS .
.It Va remote
The string form of the client's IPV4 or IVP6 address.
.It Va host
The host-name request passed to the application.
.It Va port
The server's receiving TCP port.
.It Va kdata
Internal data.
Should not be touched.
.It Va keys
Value passed to
.Fn khttp_parse .
.It Va keysz
Value passed to
.Fn khttp_parse .
.It Va pname
The script name (which may be an empty string in degenerate cases).
.El
.Pp
The application must define the
The application may optionally define
.Vt keys
provided to
.Nm khttp_parse
and
.Nm khttp_parsex
as an array of
.Vt "struct kvalid" ,
which consists of the following:
.Vt "struct kvalid" .
This structure is central to the validation of input data.
It consists of the following fields:
.Bl -tag -width Ds
.It Va "int (*valid)(struct kpair *kp)"
.It Va name
The field name, i.e., how it appears in the HTML form input name.
This cannot be
.Dv NULL .
If the field name is an empty string and the HTTP message consists of an
opaque body (and not key-value pairs), then that field will be used to
validate the HTTP message body.
This is useful for
.Dv KMETHOD_PUT
style requests.
.It Va valid
Validating function.
This can be
.Dv NULL
if anything goes.
This function accepts a single
.Ft "struct kpair *"
argument and returns an
.Ft int .
If the function is
.Dv NULL ,
then no validation is performed and the data is considered as always
valid.
If you provide your own
.Fa valid
function, it must set the
@@ -370,16 +394,6 @@ You should assume that you cannot invoke any
.Qq invasive
system calls such as opening files, sockets, etc.
In other words, these must be pure computation.
.It Va "const char *name"
The field name, i.e., how it appears in the HTML form input name.
This cannot be
.Dv NULL .
If the field name is an empty string and the HTTP message consists of an
opaque body (and not key-value pairs), then that field will be used to
validate the HTTP message body.
This is useful for
.Dv KMETHOD_PUT
style requests.
.El
.Pp
The
@@ -388,26 +402,16 @@ structure presents the user with fields parsed from input and (possibly)
matched to the
.Fa keys
variable passed to
.Fn http_parse .
.Nm khttp_parse
and
.Nm khttp_parsex .
It is also passed to the validation function to be filled in.
In this case, the MIME-related fields are already filled in and may be
examined to determine the method of validation.
This is useful when validating opaque message bodies.
.Bl -tag -width Ds
.It Va key
The nil-terminated key (input) name.
If the HTTP message body is opaque (e.g.,
.Dv KMETHOD_PUT ) ,
then an empty-string key is cooked up.
.It Va val
The (input) value, which is always nil-terminated, but if the data is
binary, nil terminators may occur before the true data length of
.Fa valsz .
.It Va valsz
The true length of
.Fa val .
.It Va file
The value's source filename or
.Dv NULL
if not defined.
.It Va ctype
The value's content type (e.g.,
The value's MIME content type (e.g.,
.Li image/jpeg ) ,
or
.Dv NULL
@@ -420,34 +424,46 @@ is not
it is looked up in the
.Fa mimes
parameter passed to
.Fn khttp_parsex
.Nm khttp_parsex
or
.Va ksuffixmap
if using
.Fn khttp_parse .
.Nm khttp_parse .
If found, it is set to the appropriate index.
Otherwise, it's
.Fa mimesz .
.It Va xcode
The value's content transfer encoding (e.g.,
.Li base64 ) ,
or
.It Va file
The value's MIME source filename or
.Dv NULL
if not defined.
.It Va key
The nil-terminated key (input) name.
If the HTTP message body is opaque (e.g.,
.Dv KMETHOD_PUT ) ,
then an empty-string key is cooked up.
.It Va keypos
If looked up in the
.Fa keys
variable passed to
.Nm khttp_parse ,
the index of the looked-up key.
Otherwise
.Fa keysz .
.It Va next
In a cookie or field map,
.Fa next
points to the next parsed key-value pair with the same
.Fa key
name.
.It Va keypos
If looked up in the
.Fa keys
variable passed to
.Fn khttp_parse ,
the index of the looked-up key.
Otherwise
.Fa keysz .
This occurs most often in HTML checkbox forms, where many fields may
have the same name.
.It Va parsed
The parsed, validated value.
These may be integer, for a 64-bit signed integer; string, for a
nil-termianted character string; or double, for a double-precision
floating-point number.
This is intentionally basic because the resulting data must be reliably
passed from the parsing context back into the web application.
.It Va state
The validation state: whether validated by a parse, invalidated by a
parse, or non-validated (unparsed).
@@ -456,11 +472,19 @@ If parsed, the type of data in
.Fa parsed ,
otherwise
.Dv KFIELD__MAX .
.It Va parsed
The parsed, validated value.
These may be integer, for a 64-bit signed integer; string, for a
nil-termianted character string; or double, for a double-precision
floating-point number.
.It Va val
The (input) value, which is always nil-terminated, but if the data is
binary, nil terminators may occur before the true data length of
.Fa valsz .
.It Va valsz
The true length of
.Fa val .
.It Va xcode
The value's MIME content transfer encoding (e.g.,
.Li base64 ) ,
or
.Dv NULL
if not defined.
.El
.Pp
Lastly, the
@@ -508,6 +532,10 @@ Opaque operating system error.
.El
.Pp
On failure, the calling application should terminate as soon as possible.
Applications should
.Em not
try to write an HTTP 505 error or similar, but allow the web server to
handle the empty CGI response on its own.
.Sh SEE ALSO
.Xr kcgi 3 ,
.Xr khttp_free 3
+101
View File
@@ -0,0 +1,101 @@
# Percentage served Time in ms
0 1.754
1 1.782
2 1.787
3 1.789
4 1.793
5 1.795
6 1.798
7 1.801
8 1.802
9 1.806
10 1.808
11 1.811
12 1.812
13 1.813
14 1.815
15 1.817
16 1.819
17 1.820
18 1.821
19 1.823
20 1.824
21 1.826
22 1.827
23 1.828
24 1.829
25 1.830
26 1.830
27 1.831
28 1.832
29 1.833
30 1.834
31 1.835
32 1.836
33 1.837
34 1.839
35 1.841
36 1.842
37 1.843
38 1.845
39 1.846
40 1.847
41 1.848
42 1.850
43 1.851
44 1.853
45 1.855
46 1.857
47 1.859
48 1.861
49 1.863
50 1.864
51 1.867
52 1.869
53 1.871
54 1.872
55 1.875
56 1.879
57 1.881
58 1.882
59 1.886
60 1.888
61 1.891
62 1.892
63 1.895
64 1.897
65 1.899
66 1.901
67 1.902
68 1.904
69 1.905
70 1.907
71 1.910
72 1.911
73 1.916
74 1.917
75 1.920
76 1.923
77 1.924
78 1.926
79 1.929
80 1.931
81 1.932
82 1.933
83 1.935
84 1.937
85 1.939
86 1.940
87 1.943
88 1.945
89 1.949
90 1.953
91 1.956
92 1.960
93 1.964
94 1.967
95 1.971
96 1.975
97 1.981
98 1.990
99 2.009
+101
View File
@@ -0,0 +1,101 @@
# Percentage served Time in ms
0 0.215
1 0.217
2 0.217
3 0.218
4 0.218
5 0.218
6 0.218
7 0.218
8 0.218
9 0.218
10 0.218
11 0.218
12 0.218
13 0.218
14 0.219
15 0.219
16 0.219
17 0.219
18 0.219
19 0.219
20 0.219
21 0.219
22 0.219
23 0.219
24 0.219
25 0.219
26 0.219
27 0.219
28 0.219
29 0.219
30 0.219
31 0.219
32 0.219
33 0.219
34 0.219
35 0.219
36 0.219
37 0.220
38 0.220
39 0.220
40 0.220
41 0.220
42 0.220
43 0.220
44 0.220
45 0.220
46 0.220
47 0.220
48 0.220
49 0.220
50 0.220
51 0.220
52 0.220
53 0.220
54 0.220
55 0.220
56 0.220
57 0.220
58 0.220
59 0.220
60 0.221
61 0.221
62 0.221
63 0.221
64 0.221
65 0.221
66 0.221
67 0.221
68 0.221
69 0.221
70 0.222
71 0.222
72 0.222
73 0.223
74 0.223
75 0.223
76 0.223
77 0.224
78 0.224
79 0.224
80 0.225
81 0.225
82 0.226
83 0.226
84 0.227
85 0.228
86 0.230
87 0.233
88 0.234
89 0.235
90 0.237
91 0.239
92 0.240
93 0.241
94 0.245
95 0.253
96 0.255
97 0.256
98 0.265
99 0.296
+101
View File
@@ -0,0 +1,101 @@
# Percentage served Time in ms
0 2.764
1 2.826
2 2.836
3 2.844
4 2.849
5 2.853
6 2.857
7 2.860
8 2.863
9 2.867
10 2.869
11 2.870
12 2.872
13 2.875
14 2.876
15 2.877
16 2.879
17 2.881
18 2.883
19 2.886
20 2.888
21 2.889
22 2.890
23 2.892
24 2.894
25 2.897
26 2.899
27 2.900
28 2.901
29 2.902
30 2.906
31 2.906
32 2.908
33 2.911
34 2.913
35 2.915
36 2.918
37 2.920
38 2.922
39 2.924
40 2.927
41 2.929
42 2.931
43 2.934
44 2.936
45 2.938
46 2.939
47 2.942
48 2.944
49 2.945
50 2.947
51 2.949
52 2.951
53 2.952
54 2.955
55 2.957
56 2.959
57 2.961
58 2.963
59 2.964
60 2.968
61 2.970
62 2.972
63 2.974
64 2.975
65 2.977
66 2.978
67 2.980
68 2.981
69 2.982
70 2.984
71 2.986
72 2.987
73 2.990
74 2.991
75 2.992
76 2.994
77 2.995
78 2.997
79 3.000
80 3.002
81 3.004
82 3.005
83 3.007
84 3.009
85 3.011
86 3.013
87 3.015
88 3.017
89 3.020
90 3.023
91 3.024
92 3.028
93 3.029
94 3.035
95 3.039
96 3.042
97 3.048
98 3.054
99 3.084
+101
View File
@@ -0,0 +1,101 @@
# Percentage served Time in ms
0 2.292
1 2.325
2 2.345
3 2.365
4 2.373
5 2.380
6 2.385
7 2.391
8 2.393
9 2.395
10 2.397
11 2.399
12 2.402
13 2.404
14 2.408
15 2.411
16 2.412
17 2.415
18 2.417
19 2.419
20 2.423
21 2.425
22 2.426
23 2.428
24 2.430
25 2.432
26 2.433
27 2.434
28 2.437
29 2.438
30 2.439
31 2.441
32 2.442
33 2.443
34 2.445
35 2.447
36 2.449
37 2.452
38 2.454
39 2.455
40 2.457
41 2.459
42 2.461
43 2.464
44 2.465
45 2.467
46 2.471
47 2.473
48 2.477
49 2.480
50 2.481
51 2.484
52 2.486
53 2.489
54 2.490
55 2.493
56 2.495
57 2.498
58 2.500
59 2.501
60 2.504
61 2.506
62 2.508
63 2.510
64 2.512
65 2.516
66 2.518
67 2.520
68 2.522
69 2.523
70 2.525
71 2.527
72 2.528
73 2.529
74 2.531
75 2.534
76 2.535
77 2.537
78 2.538
79 2.540
80 2.542
81 2.544
82 2.547
83 2.549
84 2.551
85 2.553
86 2.556
87 2.558
88 2.561
89 2.563
90 2.566
91 2.569
92 2.572
93 2.575
94 2.579
95 2.584
96 2.589
97 2.598
98 2.605
99 2.629
+101
View File
@@ -0,0 +1,101 @@
# Percentage served Time in ms
0 2.768
1 2.797
2 2.808
3 2.814
4 2.818
5 2.822
6 2.824
7 2.826
8 2.828
9 2.830
10 2.832
11 2.834
12 2.836
13 2.840
14 2.843
15 2.845
16 2.847
17 2.849
18 2.852
19 2.853
20 2.855
21 2.856
22 2.857
23 2.859
24 2.861
25 2.862
26 2.865
27 2.866
28 2.867
29 2.868
30 2.870
31 2.872
32 2.875
33 2.876
34 2.879
35 2.880
36 2.883
37 2.885
38 2.887
39 2.889
40 2.891
41 2.893
42 2.896
43 2.899
44 2.903
45 2.905
46 2.907
47 2.909
48 2.911
49 2.914
50 2.916
51 2.917
52 2.919
53 2.921
54 2.922
55 2.924
56 2.926
57 2.928
58 2.930
59 2.932
60 2.934
61 2.936
62 2.938
63 2.941
64 2.942
65 2.944
66 2.944
67 2.946
68 2.948
69 2.949
70 2.951
71 2.953
72 2.955
73 2.956
74 2.959
75 2.960
76 2.962
77 2.963
78 2.964
79 2.966
80 2.968
81 2.971
82 2.973
83 2.975
84 2.977
85 2.979
86 2.982
87 2.984
88 2.986
89 2.989
90 2.990
91 2.993
92 2.996
93 2.999
94 3.005
95 3.010
96 3.016
97 3.026
98 3.035
99 3.063
+1 -1
View File
@@ -7,7 +7,7 @@
<aside>
Update the included sample file and correct the documentation regarding its compilation.
Fix noted by Jan Schreiber &#8212; thanks!
While doing so, add some documentation bits to the manpages.
While doing so, add some documentation bits to the manpages and considerable documentation to the webpage.
No functional changes.
</aside>
</article>