mirror of https://git.kore.io/kore.git
Cookies and arguments parsing improvements (#166)
Add new cookie API for handling of cookies.
This commit is contained in:
parent
23d5e9b341
commit
f4ac8c2955
|
@ -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
|
||||||
|
```
|
|
@ -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.
|
||||||
|
#}
|
|
@ -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
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -39,6 +39,10 @@ extern "C" {
|
||||||
#define HTTP_REQ_HEADER_MAX 25
|
#define HTTP_REQ_HEADER_MAX 25
|
||||||
#define HTTP_MAX_QUERY_ARGS 20
|
#define HTTP_MAX_QUERY_ARGS 20
|
||||||
#define HTTP_MAX_COOKIES 10
|
#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_REQUEST_LIMIT 1000
|
||||||
#define HTTP_BODY_DISK_PATH "tmp_files"
|
#define HTTP_BODY_DISK_PATH "tmp_files"
|
||||||
#define HTTP_BODY_DISK_OFFLOAD 0
|
#define HTTP_BODY_DISK_OFFLOAD 0
|
||||||
|
@ -67,6 +71,22 @@ struct http_header {
|
||||||
TAILQ_ENTRY(http_header) list;
|
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 {
|
struct http_arg {
|
||||||
char *name;
|
char *name;
|
||||||
char *s_value;
|
char *s_value;
|
||||||
|
@ -199,6 +219,8 @@ struct http_request {
|
||||||
LIST_HEAD(, kore_task) tasks;
|
LIST_HEAD(, kore_task) tasks;
|
||||||
LIST_HEAD(, kore_pgsql) pgsqls;
|
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) req_headers;
|
||||||
TAILQ_HEAD(, http_header) resp_headers;
|
TAILQ_HEAD(, http_header) resp_headers;
|
||||||
TAILQ_HEAD(, http_arg) arguments;
|
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 *);
|
size_t, int (*cb)(struct netbuf *), void *);
|
||||||
int http_request_header(struct http_request *,
|
int http_request_header(struct http_request *,
|
||||||
const char *, char **);
|
const char *, char **);
|
||||||
|
int http_request_cookie(struct http_request *,
|
||||||
|
const char *, char **);
|
||||||
void http_response_header(struct http_request *,
|
void http_response_header(struct http_request *,
|
||||||
const char *, const char *);
|
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 *,
|
int http_request_new(struct connection *, const char *,
|
||||||
const char *, const char *, const char *,
|
const char *, const char *, const char *,
|
||||||
struct http_request **);
|
struct http_request **);
|
||||||
|
@ -255,6 +281,7 @@ int http_header_recv(struct netbuf *);
|
||||||
void http_populate_get(struct http_request *);
|
void http_populate_get(struct http_request *);
|
||||||
void http_populate_post(struct http_request *);
|
void http_populate_post(struct http_request *);
|
||||||
void http_populate_multipart_form(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 *,
|
int http_argument_get(struct http_request *,
|
||||||
const char *, void **, void *, int);
|
const char *, void **, void *, int);
|
||||||
|
|
||||||
|
|
139
src/http.c
139
src/http.c
|
@ -54,12 +54,14 @@ static int multipart_parse_headers(struct http_request *,
|
||||||
const char *, const int);
|
const char *, const int);
|
||||||
|
|
||||||
static struct kore_buf *header_buf;
|
static struct kore_buf *header_buf;
|
||||||
|
static struct kore_buf *ckhdr_buf;
|
||||||
static char http_version[32];
|
static char http_version[32];
|
||||||
static u_int16_t http_version_len;
|
static u_int16_t http_version_len;
|
||||||
static TAILQ_HEAD(, http_request) http_requests;
|
static TAILQ_HEAD(, http_request) http_requests;
|
||||||
static TAILQ_HEAD(, http_request) http_requests_sleeping;
|
static TAILQ_HEAD(, http_request) http_requests_sleeping;
|
||||||
static struct kore_pool http_request_pool;
|
static struct kore_pool http_request_pool;
|
||||||
static struct kore_pool http_header_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_host_pool;
|
||||||
static struct kore_pool http_path_pool;
|
static struct kore_pool http_path_pool;
|
||||||
static struct kore_pool http_body_path;
|
static struct kore_pool http_body_path;
|
||||||
|
@ -81,7 +83,8 @@ http_init(void)
|
||||||
TAILQ_INIT(&http_requests);
|
TAILQ_INIT(&http_requests);
|
||||||
TAILQ_INIT(&http_requests_sleeping);
|
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),
|
l = snprintf(http_version, sizeof(http_version),
|
||||||
"server: kore (%d.%d.%d-%s)\r\n", KORE_VERSION_MAJOR,
|
"server: kore (%d.%d.%d-%s)\r\n", KORE_VERSION_MAJOR,
|
||||||
|
@ -96,6 +99,8 @@ http_init(void)
|
||||||
sizeof(struct http_request), prealloc);
|
sizeof(struct http_request), prealloc);
|
||||||
kore_pool_init(&http_header_pool, "http_header_pool",
|
kore_pool_init(&http_header_pool, "http_header_pool",
|
||||||
sizeof(struct http_header), prealloc * HTTP_REQ_HEADER_MAX);
|
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,
|
kore_pool_init(&http_host_pool,
|
||||||
"http_host_pool", KORE_DOMAINNAME_LEN, prealloc);
|
"http_host_pool", KORE_DOMAINNAME_LEN, prealloc);
|
||||||
|
@ -113,6 +118,11 @@ http_cleanup(void)
|
||||||
header_buf = NULL;
|
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_request_pool);
|
||||||
kore_pool_cleanup(&http_header_pool);
|
kore_pool_cleanup(&http_header_pool);
|
||||||
kore_pool_cleanup(&http_host_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->resp_headers));
|
||||||
TAILQ_INIT(&(req->req_headers));
|
TAILQ_INIT(&(req->req_headers));
|
||||||
|
TAILQ_INIT(&(req->resp_cookies));
|
||||||
|
TAILQ_INIT(&(req->req_cookies));
|
||||||
TAILQ_INIT(&(req->arguments));
|
TAILQ_INIT(&(req->arguments));
|
||||||
TAILQ_INIT(&(req->files));
|
TAILQ_INIT(&(req->files));
|
||||||
|
|
||||||
|
@ -413,6 +425,7 @@ http_request_free(struct http_request *req)
|
||||||
struct http_file *f, *fnext;
|
struct http_file *f, *fnext;
|
||||||
struct http_arg *q, *qnext;
|
struct http_arg *q, *qnext;
|
||||||
struct http_header *hdr, *next;
|
struct http_header *hdr, *next;
|
||||||
|
struct http_cookie *ck, *cknext;
|
||||||
|
|
||||||
#if defined(KORE_USE_TASKS)
|
#if defined(KORE_USE_TASKS)
|
||||||
pending_tasks = 0;
|
pending_tasks = 0;
|
||||||
|
@ -474,6 +487,26 @@ http_request_free(struct http_request *req)
|
||||||
kore_pool_put(&http_header_pool, hdr);
|
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) {
|
for (q = TAILQ_FIRST(&(req->arguments)); q != NULL; q = qnext) {
|
||||||
qnext = TAILQ_NEXT(q, list);
|
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);
|
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
|
int
|
||||||
http_header_recv(struct netbuf *nb)
|
http_header_recv(struct netbuf *nb)
|
||||||
{
|
{
|
||||||
|
@ -965,6 +1013,60 @@ http_file_rewind(struct http_file *file)
|
||||||
file->offset = 0;
|
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
|
void
|
||||||
http_populate_post(struct http_request *req)
|
http_populate_post(struct http_request *req)
|
||||||
{
|
{
|
||||||
|
@ -1457,7 +1559,10 @@ http_response_normal(struct http_request *req, struct connection *c,
|
||||||
int status, const void *d, size_t len)
|
int status, const void *d, size_t len)
|
||||||
{
|
{
|
||||||
struct http_header *hdr;
|
struct http_header *hdr;
|
||||||
|
struct http_cookie *ck;
|
||||||
|
struct tm expires_tm;
|
||||||
char *conn;
|
char *conn;
|
||||||
|
char expires[HTTP_DATE_MAXSIZE];
|
||||||
int connection_close;
|
int connection_close;
|
||||||
|
|
||||||
header_buf->offset = 0;
|
header_buf->offset = 0;
|
||||||
|
@ -1500,6 +1605,38 @@ http_response_normal(struct http_request *req, struct connection *c,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req != NULL) {
|
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) {
|
TAILQ_FOREACH(hdr, &(req->resp_headers), list) {
|
||||||
kore_buf_appendf(header_buf, "%s: %s\r\n",
|
kore_buf_appendf(header_buf, "%s: %s\r\n",
|
||||||
hdr->header, hdr->value);
|
hdr->header, hdr->value);
|
||||||
|
|
Loading…
Reference in New Issue