Cookies and arguments parsing improvements (#166)

Add new cookie API for handling of cookies.
This commit is contained in:
Stanislav Yudin 2017-02-08 07:49:10 +11:00 committed by Joris Vink
parent 23d5e9b341
commit f4ac8c2955
6 changed files with 278 additions and 3 deletions

View File

@ -0,0 +1,10 @@
This example shows cookies API usage
* Simple key value cookie
* Complex cookie with RFC 6265 features
* Mix with cookie formatted in the header
Run:
```
# kore run
```

View File

@ -0,0 +1,18 @@
# generic build config
# You can switch flavors using: kore flavor [newflavor]
# The cflags below are shared between flavors
cflags=-Wall -Wmissing-declarations -Wshadow
cflags=-Wstrict-prototypes -Wmissing-prototypes
cflags=-Wpointer-arith -Wcast-qual -Wsign-compare
dev {
# These cflags are added to the shared ones when
# you build the "dev" flavor.
cflags=-g
}
#prod {
# You can specify additional CFLAGS here which are only
# included if you build with the "prod" flavor.
#}

View File

@ -0,0 +1,20 @@
# Placeholder configuration
bind 127.0.0.1 8888
load ./cookies.so
tls_dhparam dh2048.pem
http_body_max 1024000000
http_body_disk_offload 1024000
domain 127.0.0.1 {
certfile cert/server.crt
certkey cert/server.key
accesslog kore_access.log
static / serve_cookies
static /secure serve_cookies
static /vault serve_cookies
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2017 Stanislav Yudin <stan@endlessinsomnia.com>
*
* 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 <kore/kore.h>
#include <kore/http.h>
#include <openssl/sha.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
static char *html = "<html><body><h1>Reload this page</h1></body></html>";
int serve_cookies(struct http_request *);
int
serve_cookies(struct http_request *req)
{
char *read;
struct http_cookie *complex;
http_populate_cookies(req);
if (http_request_cookie(req, "Simple", &read)) {
kore_log(LOG_DEBUG, "Got simple: %s", read);
}
if (http_request_cookie(req, "Complex", &read)) {
kore_log(LOG_DEBUG, "Got complex: %s", read);
}
if (http_request_cookie(req, "Formatted", &read)) {
kore_log(LOG_DEBUG, "Got formatted: %s", read);
}
/* set simple cookie */
http_response_cookie(req, "Simple", "Hello World!", HTTP_COOKIE_DEFAULT);
/* set complex cookie */
complex = http_response_cookie(req, "Complex", "Secure Value!",
HTTP_COOKIE_HTTPONLY | HTTP_COOKIE_SECURE);
complex->path = kore_strdup("/secure");
complex->expires = time(NULL) + 1 * 60 * 60;
complex->domain = kore_strdup("127.0.0.1");
/* set formatted cookie */
http_response_header(req, "set-cookie",
"Formatted=TheValue; Path=/vault; HttpOnly");
http_response(req, 200, html, strlen(html));
return KORE_RESULT_OK;
}

View File

@ -39,6 +39,10 @@ extern "C" {
#define HTTP_REQ_HEADER_MAX 25
#define HTTP_MAX_QUERY_ARGS 20
#define HTTP_MAX_COOKIES 10
#define HTTP_MAX_COOKIENAME 255
#define HTTP_HEADER_BUFSIZE 1024
#define HTTP_COOKIE_BUFSIZE 1024
#define HTTP_DATE_MAXSIZE 255
#define HTTP_REQUEST_LIMIT 1000
#define HTTP_BODY_DISK_PATH "tmp_files"
#define HTTP_BODY_DISK_OFFLOAD 0
@ -67,6 +71,22 @@ struct http_header {
TAILQ_ENTRY(http_header) list;
};
#define HTTP_COOKIE_DEFAULT 0x0000
#define HTTP_COOKIE_HTTPONLY 0x0001
#define HTTP_COOKIE_SECURE 0x0002
struct http_cookie {
char *name;
char *value;
char *path;
char *domain;
u_int32_t maxage;
time_t expires;
u_int16_t flags;
TAILQ_ENTRY(http_cookie) list;
};
struct http_arg {
char *name;
char *s_value;
@ -199,6 +219,8 @@ struct http_request {
LIST_HEAD(, kore_task) tasks;
LIST_HEAD(, kore_pgsql) pgsqls;
TAILQ_HEAD(, http_cookie) req_cookies;
TAILQ_HEAD(, http_cookie) resp_cookies;
TAILQ_HEAD(, http_header) req_headers;
TAILQ_HEAD(, http_header) resp_headers;
TAILQ_HEAD(, http_arg) arguments;
@ -242,8 +264,12 @@ void http_response_stream(struct http_request *, int, void *,
size_t, int (*cb)(struct netbuf *), void *);
int http_request_header(struct http_request *,
const char *, char **);
int http_request_cookie(struct http_request *,
const char *, char **);
void http_response_header(struct http_request *,
const char *, const char *);
struct http_cookie *http_response_cookie(struct http_request *,
char *, char *, u_int16_t);
int http_request_new(struct connection *, const char *,
const char *, const char *, const char *,
struct http_request **);
@ -255,6 +281,7 @@ int http_header_recv(struct netbuf *);
void http_populate_get(struct http_request *);
void http_populate_post(struct http_request *);
void http_populate_multipart_form(struct http_request *);
void http_populate_cookies(struct http_request *);
int http_argument_get(struct http_request *,
const char *, void **, void *, int);

View File

@ -54,12 +54,14 @@ static int multipart_parse_headers(struct http_request *,
const char *, const int);
static struct kore_buf *header_buf;
static struct kore_buf *ckhdr_buf;
static char http_version[32];
static u_int16_t http_version_len;
static TAILQ_HEAD(, http_request) http_requests;
static TAILQ_HEAD(, http_request) http_requests_sleeping;
static struct kore_pool http_request_pool;
static struct kore_pool http_header_pool;
static struct kore_pool http_cookie_pool;
static struct kore_pool http_host_pool;
static struct kore_pool http_path_pool;
static struct kore_pool http_body_path;
@ -81,7 +83,8 @@ http_init(void)
TAILQ_INIT(&http_requests);
TAILQ_INIT(&http_requests_sleeping);
header_buf = kore_buf_alloc(1024);
header_buf = kore_buf_alloc(HTTP_HEADER_BUFSIZE);
ckhdr_buf = kore_buf_alloc(HTTP_COOKIE_BUFSIZE);
l = snprintf(http_version, sizeof(http_version),
"server: kore (%d.%d.%d-%s)\r\n", KORE_VERSION_MAJOR,
@ -96,6 +99,8 @@ http_init(void)
sizeof(struct http_request), prealloc);
kore_pool_init(&http_header_pool, "http_header_pool",
sizeof(struct http_header), prealloc * HTTP_REQ_HEADER_MAX);
kore_pool_init(&http_cookie_pool, "http_cookie_pool",
sizeof(struct http_cookie), prealloc * HTTP_MAX_COOKIES);
kore_pool_init(&http_host_pool,
"http_host_pool", KORE_DOMAINNAME_LEN, prealloc);
@ -113,6 +118,11 @@ http_cleanup(void)
header_buf = NULL;
}
if (ckhdr_buf != NULL) {
kore_buf_free(ckhdr_buf);
ckhdr_buf = NULL;
}
kore_pool_cleanup(&http_request_pool);
kore_pool_cleanup(&http_header_pool);
kore_pool_cleanup(&http_host_pool);
@ -255,6 +265,8 @@ http_request_new(struct connection *c, const char *host,
TAILQ_INIT(&(req->resp_headers));
TAILQ_INIT(&(req->req_headers));
TAILQ_INIT(&(req->resp_cookies));
TAILQ_INIT(&(req->req_cookies));
TAILQ_INIT(&(req->arguments));
TAILQ_INIT(&(req->files));
@ -413,6 +425,7 @@ http_request_free(struct http_request *req)
struct http_file *f, *fnext;
struct http_arg *q, *qnext;
struct http_header *hdr, *next;
struct http_cookie *ck, *cknext;
#if defined(KORE_USE_TASKS)
pending_tasks = 0;
@ -474,6 +487,26 @@ http_request_free(struct http_request *req)
kore_pool_put(&http_header_pool, hdr);
}
for (ck = TAILQ_FIRST(&(req->resp_cookies)); ck != NULL; ck = cknext) {
cknext = TAILQ_NEXT(ck, list);
TAILQ_REMOVE(&(req->resp_cookies), ck, list);
kore_free(ck->name);
kore_free(ck->value);
kore_free(ck->path);
kore_free(ck->domain);
kore_pool_put(&http_cookie_pool, ck);
}
for (ck = TAILQ_FIRST(&(req->req_cookies)); ck != NULL; ck = cknext) {
cknext = TAILQ_NEXT(ck, list);
TAILQ_REMOVE(&(req->req_cookies), ck, list);
kore_free(ck->name);
kore_free(ck->value);
kore_pool_put(&http_cookie_pool, ck);
}
for (q = TAILQ_FIRST(&(req->arguments)); q != NULL; q = qnext) {
qnext = TAILQ_NEXT(q, list);
@ -601,6 +634,21 @@ http_request_header(struct http_request *req, const char *header, char **out)
return (KORE_RESULT_ERROR);
}
int
http_request_cookie(struct http_request *req, const char *cookie, char **out)
{
struct http_cookie *ck;
TAILQ_FOREACH(ck, &(req->req_cookies), list) {
if (!strcasecmp(ck->name, cookie)) {
*out = ck->value;
return (KORE_RESULT_OK);
}
}
return (KORE_RESULT_ERROR);
}
int
http_header_recv(struct netbuf *nb)
{
@ -965,6 +1013,60 @@ http_file_rewind(struct http_file *file)
file->offset = 0;
}
struct http_cookie*
http_response_cookie(struct http_request *req, char *name, char *val, u_int16_t flags)
{
if (name == NULL || strlen(name) == 0 ||
val == NULL || strlen(val) == 0) {
kore_log(LOG_ERR, "invalid cookie values to set");
return (NULL);
}
struct http_cookie *ck;
ck = kore_pool_get(&http_cookie_pool);
ck->name = kore_strdup(name);
ck->value = kore_strdup(val);
ck->flags = flags;
ck->path = NULL;
ck->domain = NULL;
ck->expires = 0;
ck->maxage = UINT32_MAX;
TAILQ_INSERT_TAIL(&(req->resp_cookies), ck, list);
return (ck);
}
void
http_populate_cookies(struct http_request *req)
{
int i, v, n;
struct http_cookie *ck;
char *c, *header;
char *cookies[HTTP_MAX_COOKIES];
char *pair[3];
if (!http_request_header(req, "cookie", &c))
return;
header = kore_strdup(c);
v = kore_split_string(header, ";", cookies, HTTP_MAX_COOKIES);
for (i = 0; i < v; i++) {
for (c = cookies[i]; isspace(*c); c++)
;
n = kore_split_string(c, "=", pair, 3);
if (n != 2) {
continue;
}
ck = kore_pool_get(&http_cookie_pool);
ck->name = kore_strdup(pair[0]);
ck->value = kore_strdup(pair[1]);
TAILQ_INSERT_TAIL(&(req->req_cookies), ck, list);
}
kore_free(header);
}
void
http_populate_post(struct http_request *req)
{
@ -1457,8 +1559,11 @@ http_response_normal(struct http_request *req, struct connection *c,
int status, const void *d, size_t len)
{
struct http_header *hdr;
char *conn;
int connection_close;
struct http_cookie *ck;
struct tm expires_tm;
char *conn;
char expires[HTTP_DATE_MAXSIZE];
int connection_close;
header_buf->offset = 0;
@ -1500,6 +1605,38 @@ http_response_normal(struct http_request *req, struct connection *c,
}
if (req != NULL) {
TAILQ_FOREACH(ck, &(req->resp_cookies), list) {
kore_buf_reset(ckhdr_buf);
kore_buf_appendf(ckhdr_buf, "%s=%s", ck->name, ck->value);
if (ck->path != NULL)
kore_buf_appendf(ckhdr_buf, "; Path=%s", ck->path);
if (ck->domain != NULL)
kore_buf_appendf(ckhdr_buf, "; Domain=%s", ck->domain);
if (ck->expires > 0) {
if (gmtime_r(&ck->expires, &expires_tm) == NULL) {
kore_log(LOG_ERR, "gmtime_r failed: %s", strerror(errno));
continue;
}
if (strftime(expires, sizeof(expires),
"%a, %d %b %y %H:%M:%S GMT", &expires_tm) == 0) {
kore_log(LOG_ERR, "strftime failed: %s", strerror(errno));
continue;
}
kore_buf_appendf(ckhdr_buf, "; Expires=%s", expires);
}
if (ck->maxage != UINT32_MAX) {
kore_buf_appendf(ckhdr_buf, "; Max-Age=%u", ck->maxage);
}
if (ck->flags & HTTP_COOKIE_HTTPONLY)
kore_buf_appendf(ckhdr_buf, "; HttpOnly");
if (ck->flags & HTTP_COOKIE_SECURE)
kore_buf_appendf(ckhdr_buf, "; Secure");
kore_buf_appendf(header_buf, "set-cookie: %s\r\n",
kore_buf_stringify(ckhdr_buf, NULL));
}
TAILQ_FOREACH(hdr, &(req->resp_headers), list) {
kore_buf_appendf(header_buf, "%s: %s\r\n",
hdr->header, hdr->value);