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