-
Notifications
You must be signed in to change notification settings - Fork 40
/
tutorial1.xml
154 lines (154 loc) · 6.49 KB
/
tutorial1.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<article data-sblg-article="1" data-sblg-tags="tutorial" itemscope="itemscope" itemtype="http://schema.org/BlogPosting">
<header>
<h2 itemprop="name">
Getting and Setting CGI Cookies in C
</h2>
<address itemprop="author"><a href="https://kristaps.bsd.lv">Kristaps Dzonsons</a></address>
<time itemprop="datePublished" datetime="2015-07-07">7 July, 2015</time>
</header>
<p>
<aside itemprop="about">
Cookies are an integral part of any web application.
In this tutorial, I'll describe how to use the HTTP header functionality in <span class="nm">kcgi</span> to set,
recognise, and store cookies.
</aside>
</p>
<h3>
Source Code
</h3>
<p>
Setting and getting cookies with <span class="nm">kcgi</span> is easy.
It uses the same logic as setting and getting form fields.
Cookies consist of name-value pairs that we can grok from a table.
I'll lead this tutorial as if we were reading a source file from top to bottom, so let's start with headers.
We'll obviously need <span class="nm">kcgi</span> and <span class="file">stdint.h</span>, which is necessary for some types
found in the header file.
</p>
<figure class="sample">
<pre class="prettyprint linenums">#include <sys/types.h> /* size_t, ssize_t */
#include <stdarg.h> /* va_list */
#include <stddef.h> /* NULL */
#include <stdint.h> /* int64_t */
#include <time.h> /* time(3) */
#include <kcgi.h></pre>
</figure>
<p>
Next, let's define identifiers for our cookies.
These will later be mapped to the cookie names and the validators for their values.
</p>
<figure class="sample">
<pre class="prettyprint linenums">enum cookie {
COOKIE_STRING,
COOKIE_INTEGER,
COOKIE__MAX
};</pre>
</figure>
<p>
The enumeration will allow us to bound an array to <code>COOKIE__MAX</code> and refer to individual buckets in the array by the
enumeration value.
I'll assume that <code>COOKIE_STRING</code> is assigned 0 and <code>COOKIE_INTEGER</code>, 1.
</p>
<p>
Next, connect the indices with validation functions and names.
The validation function is run by <a href="khttp_parse.3.html">khttp_parse(3)</a>; the name is the cookie key name.
Built-in validation functions, which we'll use, are described in <a href="kvalid_string.3.html">kvalid_string(3)</a>.
In this example, <code>kvalid_stringne</code> will validate a non-empty (nil-terminated) C string, while <code>kvalid_int</code>
will validate a signed 64-bit integer.
</p>
<figure class="sample">
<pre class="prettyprint linenums">static const struct kvalid cookies[COOKIE__MAX] = {
{ kvalid_stringne, "string" }, /* COOKIE_STRING */
{ kvalid_int, "integer" }, /* COOKIE_INTEGER */
};</pre>
</figure>
<p>
Before doing any parsing, I sanitise the HTTP context.
I'll let any page request pass, but will make sure our MIME type and HTTP method are sane.
</p>
<figure class="sample">
<pre class="prettyprint linenums">static enum khttp sanitise(const struct kreq *r) {
if (r->mime != KMIME_TEXT_HTML)
return KHTTP_404;
else if (r->method != KMETHOD_GET)
return KHTTP_405;
else
return KHTTP_200;
}</pre>
</figure>
<p>
Now the scaffolding is done.
What about the cookies?
To begin with, you should glance at the <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a>, <q>HTTP State Management
Mechanism</q>, to gain an understanding of how cookies work.
You may also want to read about the <a href="https://www.owasp.org/index.php/HttpOnly">HttpOnly</a>
and
<a href="https://www.owasp.org/index.php/SecureFlag">Secure</a> flags also available.
In our application, let's just attempt to read the cookie; and if it doesn't exist, write the cookie along with the page.
If it does exist, we'll indicate that in our page.
We'll focus only on the <code>COOKIE_STRING</code> cookie, and will set the cookie to be visible to the path root and expire in
an hour.
We'll use <a href="kutil_epoch2str.3.html">kutil_epoch2str(3)</a> to format the date.
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>.
</p>
<figure class="sample">
<pre class="prettyprint linenums">static void process(struct kreq *r) {
char buf[32];
khttp_head(r, kresps[KRESP_STATUS],
"%s", khttps[KHTTP_200]);
khttp_head(r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[r->mime]);
if (r->cookiemap[COOKIE_STRING] == NULL)
khttp_head(r, kresps[KRESP_SET_COOKIE],
"%s=%s; Path=/; expires=%s",
cookies[COOKIE_STRING].name,
"Hello, world!",
kutil_epoch2str(time(NULL) + 60 * 60,
buf, sizeof(buf)));
khttp_body(r);
khttp_puts(r,
"<!DOCTYPE html>"
"<title>Foo</title>");
if (r->cookiemap[COOKIE_STRING] != NULL)
khttp_puts(r, "Cookie found!");
else
khttp_puts(r, "Cookie set.");
}</pre>
</figure>
<p>
Most of the above code is just to handle the HTML5 bits, and we deliberately used the smallest possible page.
(Yes, this is a valid page—<a href="https://validator.w3.org/nu/">validate</a> it yourself to find out!)
For any significant page, you'd want to use <a href="kcgihtml.3.html">kcgihtml(3)</a>.
</p>
<p>
Putting all of these together: parse the HTTP context, validate it, process it, then free the resources.
The HTTP context is closed with <a href="khttp_free.3.html">khttp_free(3)</a>.
Note that the identifiers for the cookies, <code>enum cookie</code>, are also used to identify any form input.
So if you have both form input and cookies (which is common), they can either share identifiers or use unique ones.
In other words, <code>COOKIE__MAX</code> defines the size of both <code>fieldmap</code> and <code>cookiemap</code>, so the
validator for <code>COOKIE_STRING</code> is also valid for form inputs of the same name.
</p>
<figure class="sample">
<pre class="prettyprint linenums">int main(void) {
struct kreq r;
enum khttp er;
if (khttp_parse(&r, cookies, COOKIE__MAX, NULL, 0, 0) != KCGI_OK)
return 0;
if ((er = sanitise(&r)) != KHTTP_200) {
khttp_head(&r, kresps[KRESP_STATUS],
"%s", khttps[er]);
khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
"%s", kmimetypes[KMIME_TEXT_PLAIN]);
khttp_body(&r);
if (r.mime == KMIME_TEXT_HTML)
khttp_puts(&r, "Could not service request.");
} else
process(&r);
khttp_free(&r);
return 0;
}</pre>
</figure>
<p>
For compilation, linking, and installation, see <a href="tutorial0.html">Getting Started with CGI in C</a>.
</p>
</article>