parent
0e2f6403e7
commit
43a2f82f31
28
examples/example1.c
Normal file
28
examples/example1.c
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
puts("Status: 200 OK\r");
|
||||||
|
puts("Content-Type: text/html\r");
|
||||||
|
puts("\r");
|
||||||
|
puts("Hello, world!");
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
38
examples/example2.c
Normal file
38
examples/example2.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.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!\n");
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
227
examples/example3.c
Normal file
227
examples/example3.c
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "kcgi.h"
|
||||||
|
#include "kcgihtml.h"
|
||||||
|
|
||||||
|
enum page {
|
||||||
|
PAGE_INDEX, /* /index or just / */
|
||||||
|
PAGE_SENDDATA, /* /senddata */
|
||||||
|
PAGE__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
enum key {
|
||||||
|
KEY_INTEGER,
|
||||||
|
KEY_FILE,
|
||||||
|
KEY_PAGECOUNT,
|
||||||
|
KEY_PAGESIZE,
|
||||||
|
KEY__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*disp)(struct kreq *);
|
||||||
|
|
||||||
|
static void senddata(struct kreq *);
|
||||||
|
static void sendindex(struct kreq *);
|
||||||
|
|
||||||
|
static const disp disps[PAGE__MAX] = {
|
||||||
|
sendindex, /* PAGE_INDEX */
|
||||||
|
senddata, /* PAGE_SENDDATA */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct kvalid keys[KEY__MAX] = {
|
||||||
|
{ kvalid_int, "integer" }, /* KEY_INTEGER */
|
||||||
|
{ NULL, "file" }, /* KEY_FILE */
|
||||||
|
{ kvalid_uint, "count" }, /* KEY_PAGECOUNT */
|
||||||
|
{ kvalid_uint, "size" }, /* KEY_PAGESIZE */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const pages[PAGE__MAX] = {
|
||||||
|
"index", /* PAGE_INDEX */
|
||||||
|
"senddata" /* PAGE_SENDDATA */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
resp_open(struct kreq *req, enum khttp http)
|
||||||
|
{
|
||||||
|
|
||||||
|
khttp_head(req, kresps[KRESP_STATUS],
|
||||||
|
"%s", khttps[http]);
|
||||||
|
khttp_head(req, kresps[KRESP_CONTENT_TYPE],
|
||||||
|
"%s", kmimetypes[req->mime]);
|
||||||
|
khttp_body(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
senddata(struct kreq *req)
|
||||||
|
{
|
||||||
|
int64_t i, j, k, nm, sz;
|
||||||
|
char *buf;
|
||||||
|
struct khtmlreq r;
|
||||||
|
|
||||||
|
nm = 1024 * 1024;
|
||||||
|
if (NULL != req->fieldmap[KEY_PAGECOUNT])
|
||||||
|
nm = req->fieldmap[KEY_PAGECOUNT]->parsed.i;
|
||||||
|
if (0 == nm)
|
||||||
|
nm = 1;
|
||||||
|
|
||||||
|
sz = 1;
|
||||||
|
if (NULL != req->fieldmap[KEY_PAGESIZE])
|
||||||
|
sz = req->fieldmap[KEY_PAGESIZE]->parsed.i;
|
||||||
|
if (0 == sz || (uint64_t)sz > SIZE_MAX)
|
||||||
|
sz = 1;
|
||||||
|
|
||||||
|
buf = kmalloc(sz);
|
||||||
|
|
||||||
|
resp_open(req, KHTTP_200);
|
||||||
|
khtml_open(&r, req);
|
||||||
|
khtml_elem(&r, KELEM_DOCTYPE);
|
||||||
|
khtml_elem(&r, KELEM_HTML);
|
||||||
|
khtml_elem(&r, KELEM_HEAD);
|
||||||
|
khtml_elem(&r, KELEM_TITLE);
|
||||||
|
khtml_puts(&r, "Have a banana.");
|
||||||
|
khtml_closeelem(&r, 2);
|
||||||
|
khtml_elem(&r, KELEM_BODY);
|
||||||
|
for (i = k = 0; i < nm; i++) {
|
||||||
|
for (j = 0; j < sz; j++) {
|
||||||
|
if (72 == k++) {
|
||||||
|
buf[j] = '\n';
|
||||||
|
k = 0;
|
||||||
|
} else
|
||||||
|
buf[j] = 65 + arc4random_uniform(24);
|
||||||
|
}
|
||||||
|
khtml_write(buf, sz, &r);
|
||||||
|
}
|
||||||
|
|
||||||
|
khtml_close(&r);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sendindex(struct kreq *req)
|
||||||
|
{
|
||||||
|
char *page;
|
||||||
|
struct khtmlreq r;
|
||||||
|
const char *cp;
|
||||||
|
|
||||||
|
kasprintf(&page, "%s/%s", req->pname, pages[PAGE_INDEX]);
|
||||||
|
|
||||||
|
resp_open(req, KHTTP_200);
|
||||||
|
khtml_open(&r, req);
|
||||||
|
khtml_elem(&r, KELEM_DOCTYPE);
|
||||||
|
khtml_elem(&r, KELEM_HTML);
|
||||||
|
khtml_elem(&r, KELEM_HEAD);
|
||||||
|
khtml_elem(&r, KELEM_TITLE);
|
||||||
|
khtml_puts(&r, "Welcome!");
|
||||||
|
khtml_closeelem(&r, 2);
|
||||||
|
khtml_elem(&r, KELEM_BODY);
|
||||||
|
khtml_puts(&r, "Welcome!");
|
||||||
|
khtml_attr(&r, KELEM_FORM,
|
||||||
|
KATTR_METHOD, "post",
|
||||||
|
KATTR_ENCTYPE, "multipart/form-data",
|
||||||
|
KATTR_ACTION, page,
|
||||||
|
KATTR__MAX);
|
||||||
|
khtml_elem(&r, KELEM_FIELDSET);
|
||||||
|
khtml_elem(&r, KELEM_LEGEND);
|
||||||
|
khtml_puts(&r, "Post (multipart)");
|
||||||
|
khtml_closeelem(&r, 1);
|
||||||
|
khtml_elem(&r, KELEM_P);
|
||||||
|
cp = NULL == req->fieldmap[KEY_INTEGER] ?
|
||||||
|
"" : req->fieldmap[KEY_INTEGER]->val;
|
||||||
|
khtml_attr(&r, KELEM_INPUT,
|
||||||
|
KATTR_TYPE, "number",
|
||||||
|
KATTR_NAME, keys[KEY_INTEGER].name,
|
||||||
|
KATTR_VALUE, cp, KATTR__MAX);
|
||||||
|
khtml_closeelem(&r, 1);
|
||||||
|
khtml_elem(&r, KELEM_P);
|
||||||
|
khtml_attr(&r, KELEM_INPUT,
|
||||||
|
KATTR_TYPE, "file",
|
||||||
|
KATTR_MULTIPLE, "",
|
||||||
|
KATTR_NAME, keys[KEY_FILE].name,
|
||||||
|
KATTR__MAX);
|
||||||
|
if (NULL != req->fieldmap[KEY_FILE]) {
|
||||||
|
if (NULL != req->fieldmap[KEY_FILE]->file) {
|
||||||
|
khtml_puts(&r, "file: ");
|
||||||
|
khtml_puts(&r, req->fieldmap[KEY_FILE]->file);
|
||||||
|
khtml_puts(&r, " ");
|
||||||
|
}
|
||||||
|
if (NULL != req->fieldmap[KEY_FILE]->ctype) {
|
||||||
|
khtml_puts(&r, "ctype: ");
|
||||||
|
khtml_puts(&r, req->fieldmap[KEY_FILE]->ctype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
khtml_closeelem(&r, 1);
|
||||||
|
khtml_elem(&r, KELEM_P);
|
||||||
|
khtml_attr(&r, KELEM_INPUT,
|
||||||
|
KATTR_TYPE, "submit",
|
||||||
|
KATTR__MAX);
|
||||||
|
khtml_closeelem(&r, 0);
|
||||||
|
khtml_close(&r);
|
||||||
|
free(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
struct kreq r;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up our main HTTP context.
|
||||||
|
*/
|
||||||
|
if (KCGI_OK != khttp_parse
|
||||||
|
(&r, keys, KEY__MAX, pages, PAGE__MAX, PAGE_INDEX))
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (KMETHOD_OPTIONS == r.method) {
|
||||||
|
/*
|
||||||
|
* Indicate that we accept GET and POST methods.
|
||||||
|
* This isn't really needed.
|
||||||
|
*/
|
||||||
|
khttp_head(&r, kresps[KRESP_STATUS],
|
||||||
|
"%s", khttps[KHTTP_200]);
|
||||||
|
khttp_head(&r, kresps[KRESP_ALLOW],
|
||||||
|
"OPTIONS GET POST");
|
||||||
|
khttp_body(&r);
|
||||||
|
} else if (KMETHOD_GET != r.method &&
|
||||||
|
KMETHOD_POST != r.method) {
|
||||||
|
/*
|
||||||
|
* Don't accept non-GET and non-POST methods.
|
||||||
|
*/
|
||||||
|
resp_open(&r, KHTTP_405);
|
||||||
|
} else if (PAGE__MAX == r.page ||
|
||||||
|
KMIME_TEXT_HTML != r.mime) {
|
||||||
|
/*
|
||||||
|
* We've been asked for an unknown page or something
|
||||||
|
* with an unknown extension.
|
||||||
|
*/
|
||||||
|
resp_open(&r, KHTTP_404);
|
||||||
|
khttp_puts(&r, "Page not found.");
|
||||||
|
} else
|
||||||
|
/*
|
||||||
|
* Route to page handler.
|
||||||
|
*/
|
||||||
|
(*disps[r.page])(&r);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up our context.
|
||||||
|
*/
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
328
examples/example4.c
Normal file
328
examples/example4.c
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <kcgi.h>
|
||||||
|
#include <kcgihtml.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
enum page {
|
||||||
|
PAGE_INDEX, /* /index or just / */
|
||||||
|
PAGE__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const pages[PAGE__MAX] = {
|
||||||
|
"index", /* PAGE_INDEX */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic routine to prime the HTTP document.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
resp_open(struct kreq *req, enum khttp http)
|
||||||
|
{
|
||||||
|
|
||||||
|
khttp_head(req, kresps[KRESP_STATUS],
|
||||||
|
"%s", khttps[http]);
|
||||||
|
khttp_head(req, kresps[KRESP_CONTENT_TYPE],
|
||||||
|
"%s", kmimetypes[req->mime]);
|
||||||
|
khttp_body(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the database is busy or locked, sleep for a random amount of
|
||||||
|
* time before continuing.
|
||||||
|
* We could put all sorts of heuristics in here to back off, but we do
|
||||||
|
* something really simple.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
db_sleep(size_t attempt)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (attempt < 10)
|
||||||
|
usleep(arc4random_uniform(100000));
|
||||||
|
else
|
||||||
|
usleep(arc4random_uniform(500000));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep trying to open the database forever.
|
||||||
|
*/
|
||||||
|
static sqlite3 *
|
||||||
|
db_open(void)
|
||||||
|
{
|
||||||
|
size_t attempt;
|
||||||
|
sqlite3 *db;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
attempt = 0;
|
||||||
|
again:
|
||||||
|
rc = sqlite3_open(DATADIR "/example4.db", &db);
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_open: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_open: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_OK == rc) {
|
||||||
|
sqlite3_busy_timeout(db, 500);
|
||||||
|
return(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errmsg(db));
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the database.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
db_close(sqlite3 *db)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (SQLITE_OK == sqlite3_close(db))
|
||||||
|
return;
|
||||||
|
fprintf(stderr, "sqlite3_close: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step through the result set of a database query.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
db_step(sqlite3 *db, sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
size_t attempt = 0;
|
||||||
|
|
||||||
|
again:
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_step: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_step: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SQLITE_DONE == rc || SQLITE_ROW == rc)
|
||||||
|
return(rc);
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_step: %s\n", sqlite3_errmsg(db));
|
||||||
|
return(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute a database query.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
db_exec(sqlite3 *db, const char *sql)
|
||||||
|
{
|
||||||
|
size_t attempt = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
again:
|
||||||
|
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_exec: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_exec: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_OK == rc)
|
||||||
|
return(1);
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_exec: %s (%s)\n",
|
||||||
|
sqlite3_errmsg(db), sql);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare a database statement.
|
||||||
|
* If we fail, try to populate the database: it might not have been
|
||||||
|
* initialised yet.
|
||||||
|
*/
|
||||||
|
static sqlite3_stmt *
|
||||||
|
db_stmt(sqlite3 *db, const char *sql, int recurse)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
const char *esql;
|
||||||
|
size_t attempt = 0;
|
||||||
|
int rc, bailed;
|
||||||
|
|
||||||
|
bailed = 0;
|
||||||
|
again:
|
||||||
|
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||||
|
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_prepare_v2: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_prepare_v2: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_OK == rc)
|
||||||
|
return(stmt);
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_prepare_v2: %s (%s) (%d)\n",
|
||||||
|
sqlite3_errmsg(db), sql, sqlite3_errcode(db));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only attempt to do this if (1) we have a certain error, (2)
|
||||||
|
* we haven't already bailed out, and (3) we haven't recursed
|
||||||
|
* into the db_stmt() function.
|
||||||
|
*/
|
||||||
|
if (SQLITE_ERROR == rc && 0 == bailed && recurse) {
|
||||||
|
esql = "CREATE TABLE record "
|
||||||
|
"(count INTEGER NOT NULL DEFAULT(0))";
|
||||||
|
if (db_exec(db, esql)) {
|
||||||
|
esql = "INSERT INTO record DEFAULT VALUES";
|
||||||
|
if (NULL != (stmt = db_stmt(db, esql, 0))) {
|
||||||
|
db_step(db, stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bailed = 1;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sendindex(struct kreq *req)
|
||||||
|
{
|
||||||
|
struct khtmlreq r;
|
||||||
|
int rc;
|
||||||
|
sqlite3 *db;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
resp_open(req, KHTTP_200);
|
||||||
|
khtml_open(&r, req);
|
||||||
|
khtml_elem(&r, KELEM_DOCTYPE);
|
||||||
|
khtml_elem(&r, KELEM_HTML);
|
||||||
|
khtml_elem(&r, KELEM_HEAD);
|
||||||
|
khtml_elem(&r, KELEM_TITLE);
|
||||||
|
khtml_puts(&r, "Test");
|
||||||
|
khtml_closeelem(&r, 2);
|
||||||
|
khtml_elem(&r, KELEM_BODY);
|
||||||
|
|
||||||
|
if (NULL != (db = db_open())) {
|
||||||
|
stmt = db_stmt(db, "SELECT count FROM record", 1);
|
||||||
|
if (NULL != stmt) {
|
||||||
|
rc = db_step(db, stmt);
|
||||||
|
if (SQLITE_ROW == rc)
|
||||||
|
khtml_puts(&r, (char *)
|
||||||
|
sqlite3_column_text(stmt, 0));
|
||||||
|
else if (SQLITE_DONE == rc)
|
||||||
|
khtml_puts(&r, "No value!?");
|
||||||
|
else
|
||||||
|
khtml_puts(&r, "Error getting value.");
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else
|
||||||
|
khtml_puts(&r, "Error getting statement.");
|
||||||
|
|
||||||
|
stmt = db_stmt(db, "UPDATE record SET count=count+1", 0);
|
||||||
|
(void)db_step(db, stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
db_close(db);
|
||||||
|
} else
|
||||||
|
khtml_puts(&r, "Error opening database.");
|
||||||
|
|
||||||
|
khtml_close(&r);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
struct kreq r;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up our main HTTP context.
|
||||||
|
*/
|
||||||
|
if (KCGI_OK != khttp_parse
|
||||||
|
(&r, NULL, 0, pages, PAGE__MAX, PAGE_INDEX))
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
|
||||||
|
if (KMETHOD_OPTIONS == r.method) {
|
||||||
|
/*
|
||||||
|
* Indicate that we accept GET and POST methods.
|
||||||
|
* This isn't really needed.
|
||||||
|
*/
|
||||||
|
khttp_head(&r, kresps[KRESP_STATUS],
|
||||||
|
"%s", khttps[KHTTP_200]);
|
||||||
|
khttp_head(&r, kresps[KRESP_ALLOW],
|
||||||
|
"OPTIONS GET POST");
|
||||||
|
khttp_body(&r);
|
||||||
|
} else if (KMETHOD_GET != r.method) {
|
||||||
|
/*
|
||||||
|
* Don't accept non-GET and non-POST methods.
|
||||||
|
*/
|
||||||
|
resp_open(&r, KHTTP_405);
|
||||||
|
} else if (PAGE__MAX == r.page ||
|
||||||
|
KMIME_TEXT_HTML != r.mime) {
|
||||||
|
/*
|
||||||
|
* We've been asked for an unknown page or something
|
||||||
|
* with an unknown extension.
|
||||||
|
*/
|
||||||
|
resp_open(&r, KHTTP_404);
|
||||||
|
khttp_puts(&r, "Page not found.");
|
||||||
|
} else
|
||||||
|
/*
|
||||||
|
* Route to page handler.
|
||||||
|
*/
|
||||||
|
sendindex(&r);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up our context.
|
||||||
|
*/
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
56
examples/example5.c
Normal file
56
examples/example5.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <sandbox.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
char *ep;
|
||||||
|
int rc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a sandbox that only lets us do pure computation: we
|
||||||
|
* don't open any more file descriptors or do anything fancy.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
rc = sandbox_init
|
||||||
|
(kSBXProfilePureComputation,
|
||||||
|
SANDBOX_NAMED, &ep);
|
||||||
|
if (-1 == rc) {
|
||||||
|
fprintf(stderr, "sandbox_init: %s\n", ep);
|
||||||
|
sandbox_free_error(ep);
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
if (-1 == pledge("stdio", NULL)) {
|
||||||
|
perror("pledge");
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
puts("Status: 200 OK\r");
|
||||||
|
puts("Content-Type: text/html\r");
|
||||||
|
puts("\r");
|
||||||
|
puts("Hello, world!");
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
71
examples/example6.c
Normal file
71
examples/example6.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <sandbox.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <kcgi.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
struct kreq r;
|
||||||
|
const char *page = "index";
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
char *ep;
|
||||||
|
int rc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (KCGI_OK != khttp_parse(&r, NULL, 0, &page, 1, 0))
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kcgi(3) won't do anything special with the writing routines
|
||||||
|
* except flush to the stream, possibly by way of zlib.
|
||||||
|
* Thus, enact a sandbox that allows open file descriptors but
|
||||||
|
* not anything more.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
rc = sandbox_init
|
||||||
|
(kSBXProfilePureComputation,
|
||||||
|
SANDBOX_NAMED, &ep);
|
||||||
|
if (-1 == rc) {
|
||||||
|
fprintf(stderr, "sandbox_init: %s\n", ep);
|
||||||
|
sandbox_free_error(ep);
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
if (-1 == pledge("stdio", NULL)) {
|
||||||
|
perror("pledge");
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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!\n");
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
360
examples/example7.c
Normal file
360
examples/example7.c
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <sandbox.h>
|
||||||
|
#endif
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <kcgi.h>
|
||||||
|
#include <kcgihtml.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
enum page {
|
||||||
|
PAGE_INDEX, /* /index or just / */
|
||||||
|
PAGE__MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const pages[PAGE__MAX] = {
|
||||||
|
"index", /* PAGE_INDEX */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic routine to prime the HTTP document.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
resp_open(struct kreq *req, enum khttp http)
|
||||||
|
{
|
||||||
|
|
||||||
|
khttp_head(req, kresps[KRESP_STATUS],
|
||||||
|
"%s", khttps[http]);
|
||||||
|
khttp_head(req, kresps[KRESP_CONTENT_TYPE],
|
||||||
|
"%s", kmimetypes[req->mime]);
|
||||||
|
khttp_body(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the database is busy or locked, sleep for a random amount of
|
||||||
|
* time before continuing.
|
||||||
|
* We could put all sorts of heuristics in here to back off, but we do
|
||||||
|
* something really simple.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
db_sleep(size_t attempt)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (attempt < 10)
|
||||||
|
usleep(arc4random_uniform(100000));
|
||||||
|
else
|
||||||
|
usleep(arc4random_uniform(500000));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Keep trying to open the database forever.
|
||||||
|
*/
|
||||||
|
static sqlite3 *
|
||||||
|
db_open(void)
|
||||||
|
{
|
||||||
|
size_t attempt;
|
||||||
|
sqlite3 *db;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
attempt = 0;
|
||||||
|
again:
|
||||||
|
rc = sqlite3_open(DATADIR "/example7.db", &db);
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_open: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_open: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_OK == rc) {
|
||||||
|
sqlite3_busy_timeout(db, 500);
|
||||||
|
return(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_open: %s\n", sqlite3_errmsg(db));
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the database.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
db_close(sqlite3 *db)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (SQLITE_OK == sqlite3_close(db))
|
||||||
|
return;
|
||||||
|
fprintf(stderr, "sqlite3_close: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step through the result set of a database query.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
db_step(sqlite3 *db, sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
size_t attempt = 0;
|
||||||
|
|
||||||
|
again:
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_step: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_step: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SQLITE_DONE == rc || SQLITE_ROW == rc)
|
||||||
|
return(rc);
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_step: %s\n", sqlite3_errmsg(db));
|
||||||
|
return(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute a database query.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
db_exec(sqlite3 *db, const char *sql)
|
||||||
|
{
|
||||||
|
size_t attempt = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
again:
|
||||||
|
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_exec: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_exec: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_OK == rc)
|
||||||
|
return(1);
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_exec: %s (%s)\n",
|
||||||
|
sqlite3_errmsg(db), sql);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare a database statement.
|
||||||
|
* If we fail, try to populate the database: it might not have been
|
||||||
|
* initialised yet.
|
||||||
|
*/
|
||||||
|
static sqlite3_stmt *
|
||||||
|
db_stmt(sqlite3 *db, const char *sql, int recurse)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
const char *esql;
|
||||||
|
size_t attempt = 0;
|
||||||
|
int rc, bailed;
|
||||||
|
|
||||||
|
bailed = 0;
|
||||||
|
again:
|
||||||
|
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||||
|
|
||||||
|
if (SQLITE_BUSY == rc) {
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_LOCKED == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_prepare_v2: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_PROTOCOL == rc) {
|
||||||
|
fprintf(stderr, "sqlite3_prepare_v2: %s\n",
|
||||||
|
sqlite3_errmsg(db));
|
||||||
|
db_sleep(attempt++);
|
||||||
|
goto again;
|
||||||
|
} else if (SQLITE_OK == rc)
|
||||||
|
return(stmt);
|
||||||
|
|
||||||
|
fprintf(stderr, "sqlite3_prepare_v2: %s (%s) (%d)\n",
|
||||||
|
sqlite3_errmsg(db), sql, sqlite3_errcode(db));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only attempt to do this if (1) we have a certain error, (2)
|
||||||
|
* we haven't already bailed out, and (3) we haven't recursed
|
||||||
|
* into the db_stmt() function.
|
||||||
|
*/
|
||||||
|
if (SQLITE_ERROR == rc && 0 == bailed && recurse) {
|
||||||
|
esql = "CREATE TABLE record "
|
||||||
|
"(count INTEGER NOT NULL DEFAULT(0))";
|
||||||
|
if (db_exec(db, esql)) {
|
||||||
|
esql = "INSERT INTO record DEFAULT VALUES";
|
||||||
|
if (NULL != (stmt = db_stmt(db, esql, 0))) {
|
||||||
|
db_step(db, stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esql = "PRAGMA journal_mode=WAL";
|
||||||
|
(void)db_exec(db, esql);
|
||||||
|
bailed = 1;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sendindex(struct kreq *req)
|
||||||
|
{
|
||||||
|
struct khtmlreq r;
|
||||||
|
int rc;
|
||||||
|
sqlite3 *db;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
resp_open(req, KHTTP_200);
|
||||||
|
khtml_open(&r, req);
|
||||||
|
khtml_elem(&r, KELEM_DOCTYPE);
|
||||||
|
khtml_elem(&r, KELEM_HTML);
|
||||||
|
khtml_elem(&r, KELEM_HEAD);
|
||||||
|
khtml_elem(&r, KELEM_TITLE);
|
||||||
|
khtml_puts(&r, "Test");
|
||||||
|
khtml_closeelem(&r, 2);
|
||||||
|
khtml_elem(&r, KELEM_BODY);
|
||||||
|
|
||||||
|
if (NULL != (db = db_open())) {
|
||||||
|
stmt = db_stmt(db, "SELECT count FROM record", 1);
|
||||||
|
if (NULL != stmt) {
|
||||||
|
rc = db_step(db, stmt);
|
||||||
|
if (SQLITE_ROW == rc)
|
||||||
|
khtml_puts(&r, (char *)
|
||||||
|
sqlite3_column_text(stmt, 0));
|
||||||
|
else if (SQLITE_DONE == rc)
|
||||||
|
khtml_puts(&r, "No value!?");
|
||||||
|
else
|
||||||
|
khtml_puts(&r, "Error getting value.");
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
} else
|
||||||
|
khtml_puts(&r, "Error getting statement.");
|
||||||
|
|
||||||
|
stmt = db_stmt(db, "UPDATE record SET count=count+1", 0);
|
||||||
|
(void)db_step(db, stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
db_close(db);
|
||||||
|
} else
|
||||||
|
khtml_puts(&r, "Error opening database.");
|
||||||
|
|
||||||
|
khtml_close(&r);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
struct kreq r;
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
char *ep;
|
||||||
|
int rc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up our main HTTP context.
|
||||||
|
*/
|
||||||
|
if (KCGI_OK != khttp_parse
|
||||||
|
(&r, NULL, 0, pages, PAGE__MAX, PAGE_INDEX))
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up a sandbox allowing for file-system access.
|
||||||
|
* We need this for the database, which is WAL and will thus use
|
||||||
|
* shared memory primitives.
|
||||||
|
*/
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
rc = sandbox_init
|
||||||
|
(kSBXProfileNoNetwork,
|
||||||
|
SANDBOX_NAMED, &ep);
|
||||||
|
if (-1 == rc) {
|
||||||
|
fprintf(stderr, "sandbox_init: %s\n", ep);
|
||||||
|
sandbox_free_error(ep);
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
if (-1 == pledge("stdio rpath cpath wpath flock", NULL)) {
|
||||||
|
perror("pledge");
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (KMETHOD_OPTIONS == r.method) {
|
||||||
|
/*
|
||||||
|
* Indicate that we accept GET and POST methods.
|
||||||
|
* This isn't really needed.
|
||||||
|
*/
|
||||||
|
khttp_head(&r, kresps[KRESP_STATUS],
|
||||||
|
"%s", khttps[KHTTP_200]);
|
||||||
|
khttp_head(&r, kresps[KRESP_ALLOW],
|
||||||
|
"OPTIONS GET POST");
|
||||||
|
khttp_body(&r);
|
||||||
|
} else if (KMETHOD_GET != r.method) {
|
||||||
|
/*
|
||||||
|
* Don't accept non-GET and non-POST methods.
|
||||||
|
*/
|
||||||
|
resp_open(&r, KHTTP_405);
|
||||||
|
} else if (PAGE__MAX == r.page ||
|
||||||
|
KMIME_TEXT_HTML != r.mime) {
|
||||||
|
/*
|
||||||
|
* We've been asked for an unknown page or something
|
||||||
|
* with an unknown extension.
|
||||||
|
*/
|
||||||
|
resp_open(&r, KHTTP_404);
|
||||||
|
khttp_puts(&r, "Page not found.");
|
||||||
|
} else
|
||||||
|
/*
|
||||||
|
* Route to page handler.
|
||||||
|
*/
|
||||||
|
sendindex(&r);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up our context.
|
||||||
|
*/
|
||||||
|
khttp_free(&r);
|
||||||
|
return(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user