aboutsummaryrefslogtreecommitdiff
path: root/lib/roles
diff options
context:
space:
mode:
authorYichen Gu <guyiche@amazon.com>2021-07-05 16:41:41 +0800
committerAndy Green <andy@warmcat.com>2021-08-21 17:44:40 +0100
commitb31c5d6ffe0b80811f3348a8a51b508d757c0f32 (patch)
treec41a5b65b9fca9b6e9914f755631a4b483a02229 /lib/roles
parentb67d192100c37568d81099ad9aea2264b342e0ae (diff)
downloadlibwebsockets-b31c5d6ffe0b80811f3348a8a51b508d757c0f32.tar.gz
http: cookies: support cookie jar in and out
Diffstat (limited to 'lib/roles')
-rw-r--r--lib/roles/h2/hpack.c17
-rw-r--r--lib/roles/h2/http2.c5
-rw-r--r--lib/roles/http/CMakeLists.txt7
-rw-r--r--lib/roles/http/client/client-http.c12
-rw-r--r--lib/roles/http/cookie.c729
-rw-r--r--lib/roles/http/header.c5
6 files changed, 766 insertions, 9 deletions
diff --git a/lib/roles/h2/hpack.c b/lib/roles/h2/hpack.c
index 68629e6f..f6aefd34 100644
--- a/lib/roles/h2/hpack.c
+++ b/lib/roles/h2/hpack.c
@@ -1398,11 +1398,15 @@ int lws_add_http2_header_by_name(struct lws *wsi, const unsigned char *name,
#if defined(_DEBUG)
/* value does not have to be NUL-terminated... %.*s not available on
* all platforms */
- lws_strnncpy((char *)*p, (const char *)value, length,
- lws_ptr_diff(end, (*p)));
-
- lwsl_header("%s: %p %s:%s (len %d)\n", __func__, *p, name,
- (const char *)*p, length);
+ if (value) {
+ lws_strnncpy((char *)*p, (const char *)value, length,
+ lws_ptr_diff(end, (*p)));
+
+ lwsl_header("%s: %p %s:%s (len %d)\n", __func__, *p, name,
+ (const char *)*p, length);
+ } else {
+ lwsl_err("%s: %p dummy copy %s (len %d)\n", __func__, *p, name, length);
+ }
#endif
len = (int)strlen((char *)name);
@@ -1436,7 +1440,8 @@ int lws_add_http2_header_by_name(struct lws *wsi, const unsigned char *name,
if (lws_h2_num(7, (unsigned long)length, p, end))
return 1;
- memcpy(*p, value, (unsigned int)length);
+ if (value)
+ memcpy(*p, value, (unsigned int)length);
*p += length;
return 0;
diff --git a/lib/roles/h2/http2.c b/lib/roles/h2/http2.c
index 9ae46d95..21b605cc 100644
--- a/lib/roles/h2/http2.c
+++ b/lib/roles/h2/http2.c
@@ -2625,6 +2625,11 @@ lws_h2_client_handshake(struct lws *wsi)
/* give userland a chance to append, eg, cookies */
+#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
+ if (wsi->flags & LCCSCF_CACHE_COOKIES)
+ lws_cookie_send_cookies(wsi, (char **)&p, (char *)end);
+#endif
+
if (wsi->a.protocol->callback(wsi,
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
wsi->user_space, &p, lws_ptr_diff_size_t(end, p) - 12))
diff --git a/lib/roles/http/CMakeLists.txt b/lib/roles/http/CMakeLists.txt
index 706823ab..01ad79b9 100644
--- a/lib/roles/http/CMakeLists.txt
+++ b/lib/roles/http/CMakeLists.txt
@@ -35,13 +35,18 @@ list(APPEND SOURCES
roles/http/header.c
roles/http/date.c
roles/http/parsers.c)
-
+
if (NOT LWS_WITHOUT_SERVER)
list(APPEND SOURCES
roles/http/server/server.c
roles/http/server/lws-spa.c)
endif()
+if (LWS_WITH_CACHE_NSCOOKIEJAR AND LWS_WITH_CLIENT)
+ list(APPEND SOURCES
+ roles/http/cookie.c)
+endif()
+
if (LWS_WITH_HTTP_PROXY AND LWS_WITH_HUBBUB)
list(APPEND SOURCES
roles/http/server/rewrite.c)
diff --git a/lib/roles/http/client/client-http.c b/lib/roles/http/client/client-http.c
index 99f270b6..221f394d 100644
--- a/lib/roles/http/client/client-http.c
+++ b/lib/roles/http/client/client-http.c
@@ -635,6 +635,13 @@ lws_client_interpret_server_handshake(struct lws *wsi)
ah->http_response = 0;
}
+#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
+
+ if ((wsi->flags & LCCSCF_CACHE_COOKIES) &&
+ lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_SET_COOKIE))
+ lws_parse_set_cookie(wsi);
+
+#endif
/*
* well, what the server sent looked reasonable for syntax.
* Now let's confirm it sent all the necessary headers
@@ -1269,6 +1276,11 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
/* give userland a chance to append, eg, cookies */
+#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
+ if (wsi->flags & LCCSCF_CACHE_COOKIES)
+ lws_cookie_send_cookies(wsi, &p, end);
+#endif
+
if (wsi->a.protocol->callback(wsi,
LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER,
wsi->user_space, &p,
diff --git a/lib/roles/http/cookie.c b/lib/roles/http/cookie.c
new file mode 100644
index 00000000..03b88ae0
--- /dev/null
+++ b/lib/roles/http/cookie.c
@@ -0,0 +1,729 @@
+
+#include <libwebsockets.h>
+#include "private-lib-core.h"
+
+//#define LWS_COOKIE_DEBUG
+
+#if defined(LWS_COOKIE_DEBUG)
+ #define lwsl_cookie lwsl_notice
+#else
+ #define lwsl_cookie lwsl_debug
+#endif
+
+#define LWS_COOKIE_MAX_CACHE_NAME_LEN 128
+
+#define lws_tolower(_c) (((_c) >= 'A' && (_c) <= 'Z') ? \
+ (char)((_c) + 'a' - 'A') : \
+ (char)(_c))
+
+#define LWS_COOKIE_NSC_FORMAT "%.*s\t"\
+ "%s\t"\
+ "%.*s\t"\
+ "%s\t"\
+ "%llu\t"\
+ "%.*s\t"\
+ "%.*s"
+
+static const char *const mon = "janfebmaraprnayjunjulaugsepoctnovdec";
+
+enum lws_cookie_nsc_f {
+ LWSC_NSC_DOMAIN,
+ LWSC_NSC_HOSTONLY,
+ LWSC_NSC_PATH,
+ LWSC_NSC_SECURE,
+ LWSC_NSC_EXPIRES,
+ LWSC_NSC_NAME,
+ LWSC_NSC_VALUE,
+
+ LWSC_NSC_COUNT,
+};
+
+enum lws_cookie_elements {
+ CE_DOMAIN,
+ CE_PATH,
+ CE_EXPIRES,
+ CE_MAXAGE,
+ CE_NAME,
+ CE_VALUE,
+
+ CE_HOSTONLY, /* these are bool, NULL = 0, non-NULL = 1 */
+ CE_SECURE,
+
+ CE_COUNT
+};
+
+struct lws_cookie {
+ const char *f[CE_COUNT];
+ size_t l[CE_COUNT];
+
+ unsigned int httponly:1;
+};
+
+static int
+lws_cookie_parse_date(const char *d, size_t len, time_t *t)
+{
+ struct tm date;
+ int offset = 0, i;
+
+ memset(&date, 0, sizeof(date));
+
+ while (len) {
+ if (isalnum((int)*d)) {
+ offset++;
+ goto next;
+ }
+ switch (offset) {
+ case 2:
+ if (*d == ':' && len >= 6) {
+ date.tm_hour = atoi(d - 2);
+ if (date.tm_hour < 0 || date.tm_hour > 23)
+ return -1;
+ date.tm_min = atoi(d + 1);
+ if (date.tm_min < 0 || date.tm_min > 60)
+ return -1;
+ date.tm_sec = atoi(d + 4);
+ if (date.tm_sec < 0 || date.tm_sec > 61)
+ /* leap second */
+ return -1;
+
+ d += 6;
+ len -= 6;
+ offset = 0;
+ continue;
+ }
+
+ if (!date.tm_mday) {
+ date.tm_mday = atoi(d - 2);
+ if (date.tm_mday < 1 || date.tm_mday > 31)
+ return -1;
+ goto next2;
+ }
+
+ if (!date.tm_year) {
+ date.tm_year = atoi(d - 2);
+ if (date.tm_year < 0 || date.tm_year > 99)
+ return -1;
+ if (date.tm_year < 70)
+ date.tm_year += 100;
+ }
+ goto next2;
+
+ case 3:
+ for (i = 0; i < 36; i += 3) {
+ if (lws_tolower(*(d - 3)) == mon[i] &&
+ lws_tolower(*(d - 2)) == mon[i + 1] &&
+ lws_tolower(*(d - 1)) == mon[i + 2]) {
+ date.tm_mon = i / 3;
+ break;
+ }
+ }
+ goto next2;
+
+ case 4:
+ if (!date.tm_year) {
+ date.tm_year = atoi(d - 4);
+ if (date.tm_year < 1601)
+ return -1;
+ date.tm_year -= 1900;
+ }
+ goto next2;
+
+ default:
+ goto next2;
+ }
+
+next2:
+ offset = 0;
+next:
+ d++;
+ len--;
+ }
+
+ *t = mktime(&date);
+
+ if (*t < 0)
+ return -1;
+
+ return 0;
+}
+
+static void
+lws_cookie_rm_sws(const char **buf_p, size_t *len_p)
+{
+ const char *buf;
+ size_t len;
+
+ if (!buf_p || !*buf_p || !len_p || !*len_p) {
+ lwsl_err("%s: false parameter\n", __func__);
+ return;
+ }
+
+ buf = *buf_p;
+ len = *len_p;
+ while (buf[0] == ' ' && len > 0) {
+ buf++;
+ len--;
+ }
+ while (buf[len - 1] == ' ' && len > 0)
+ len--;
+
+ *buf_p = buf;
+ *len_p = len;
+}
+
+static int
+is_iprefix(const char *h, size_t hl, const char *n, size_t nl)
+{
+ if (!h || !n || nl > hl)
+ return 0;
+
+ while (nl) {
+ nl--;
+ if (lws_tolower(h[nl]) != lws_tolower(n[nl]))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+lws_cookie_compile_cache_name(char *buf, size_t buf_len, struct lws_cookie *c)
+{
+ if (!buf || !c->f[CE_DOMAIN] || !c->f[CE_PATH] || !c->f[CE_NAME] ||
+ c->l[CE_DOMAIN] + c->l[CE_PATH] + c->l[CE_NAME] + 6 > buf_len)
+ return -1;
+
+ memcpy(buf, c->f[CE_DOMAIN], c->l[CE_DOMAIN]);
+ buf += c->l[CE_DOMAIN];
+ *buf++ = '|';
+
+ memcpy(buf, c->f[CE_PATH], c->l[CE_PATH]);
+ buf += c->l[CE_PATH];
+ *buf++ = '|';
+
+ memcpy(buf, c->f[CE_NAME], c->l[CE_NAME]);
+ buf += c->l[CE_NAME];
+ *buf = '\0';
+
+ return 0;
+}
+
+static int
+lws_cookie_parse_nsc(struct lws_cookie *c, const char *b, size_t l)
+{
+ enum lws_cookie_nsc_f state = LWSC_NSC_DOMAIN;
+ size_t n = 0;
+
+ if (!c || !b || l < 13)
+ return -1;
+
+ memset(c, 0, sizeof(*c));
+ lwsl_cookie("%s: parsing (%.*s) \n", __func__, (int)l, b);
+
+ while (l) {
+ l--;
+ if (b[n] != '\t' && l) {
+ n++;
+ continue;
+ }
+ switch (state) {
+ case LWSC_NSC_DOMAIN:
+ c->f[CE_DOMAIN] = b;
+ c->l[CE_DOMAIN] = n;
+ break;
+ case LWSC_NSC_PATH:
+ c->f[CE_PATH] = b;
+ c->l[CE_PATH] = n;
+ break;
+ case LWSC_NSC_EXPIRES:
+ c->f[CE_EXPIRES] = b;
+ c->l[CE_EXPIRES] = n;
+ break;
+ case LWSC_NSC_NAME:
+ c->f[CE_NAME] = b;
+ c->l[CE_NAME] = n;
+ break;
+
+ case LWSC_NSC_HOSTONLY:
+ if (b[0] == 'T') {
+ c->f[CE_HOSTONLY] = b;
+ c->l[CE_HOSTONLY] = 1;
+ }
+ break;
+ case LWSC_NSC_SECURE:
+ if (b[0] == 'T') {
+ c->f[CE_SECURE] = b;
+ c->l[CE_SECURE] = 1;
+ }
+ break;
+
+ case LWSC_NSC_VALUE:
+ c->f[CE_VALUE] = b;
+ c->l[CE_VALUE] = n + 1;
+
+ for (n = 0; n < LWS_ARRAY_SIZE(c->f); n++)
+ lwsl_cookie("%s: %d: %.*s\n", __func__,
+ (int)n, (int)c->l[n], c->f[n]);
+
+ return 0;
+ default:
+ return -1;
+ }
+
+ b += n + 1;
+ n = 0;
+ state++;
+ }
+
+ return -1;
+}
+
+static int
+lws_cookie_write_nsc(struct lws *wsi, struct lws_cookie *c)
+{
+ char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
+ struct lws_cache_ttl_lru *l1;
+ struct client_info_stash *stash;
+ char *cookie_string = NULL, *dl;
+ /* 6 tabs + 20 for max time_t + 2 * TRUE/FALSE + null */
+ size_t size = 6 + 20 + 10 + 1;
+ time_t expires = 0;
+ int ret = 0;
+
+ if (!wsi || !c)
+ return -1;
+
+ l1 = wsi->a.context->l1;
+ if (!l1 || !wsi->a.context->nsc)
+ return -1;
+
+ stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
+ if (!stash || !stash->cis[CIS_ADDRESS] ||
+ !stash->cis[CIS_PATH])
+ return -1;
+
+
+ if (!c->f[CE_NAME] || !c->f[CE_VALUE]) {
+ lwsl_err("%s: malformed c\n", __func__);
+
+ return -1;
+ }
+
+ if (!c->f[CE_EXPIRES]) {
+ /*
+ * Currently we just take the approach to reject session cookies
+ */
+ lwsl_warn("%s: reject session cookies\n", __func__);
+
+ return 0;
+ }
+
+ if (!c->f[CE_DOMAIN]) {
+ c->f[CE_HOSTONLY] = "T";
+ c->l[CE_HOSTONLY] = 1;
+ c->f[CE_DOMAIN] = stash->cis[CIS_ADDRESS];
+ c->l[CE_DOMAIN] = strlen(c->f[CE_DOMAIN]);
+ }
+
+ if (!c->f[CE_PATH]) {
+ c->f[CE_PATH] = stash->cis[CIS_PATH];
+ c->l[CE_PATH] = strlen(c->f[CE_PATH]);
+ dl = memchr(c->f[CE_PATH], '?', c->l[CE_PATH]);
+ if (dl)
+ c->l[CE_PATH] = (size_t)(dl - c->f[CE_PATH]);
+ }
+
+ if (lws_cookie_compile_cache_name(cache_name, sizeof(cache_name), c))
+ return -1;
+
+ if (c->f[CE_EXPIRES] &&
+ lws_cookie_parse_date(c->f[CE_EXPIRES], c->l[CE_EXPIRES], &expires)) {
+ lwsl_err("%s: can't parse date %.*s\n", __func__,
+ (int)c->l[CE_EXPIRES], c->f[CE_EXPIRES]);
+ return -1;
+ }
+
+ size += c->l[CE_NAME] + c->l[CE_VALUE] + c->l[CE_DOMAIN] + c->l[CE_PATH];
+ cookie_string = (char *)lws_malloc(size, __func__);
+ if (!cookie_string) {
+ lwsl_err("%s: OOM\n",__func__);
+
+ return -1;
+ }
+
+ lws_snprintf(cookie_string, size, LWS_COOKIE_NSC_FORMAT,
+ (int)c->l[CE_DOMAIN], c->f[CE_DOMAIN],
+ c->f[CE_HOSTONLY] ? "TRUE" : "FALSE",
+ (int)c->l[CE_PATH], c->f[CE_PATH],
+ c->f[CE_SECURE] ? "TRUE" : "FALSE",
+ (unsigned long long)expires,
+ (int)c->l[CE_NAME], c->f[CE_NAME],
+ (int)c->l[CE_VALUE], c->f[CE_VALUE]);
+
+ lwsl_cookie("%s: name %s\n", __func__, cache_name);
+ lwsl_cookie("%s: c %s\n", __func__, cookie_string);
+
+ if (lws_cache_write_through(l1, cache_name,
+ (const uint8_t *)cookie_string,
+ strlen(cookie_string),
+ (lws_usec_t)((unsigned long long)expires *
+ (lws_usec_t)LWS_US_PER_SEC), NULL)) {
+ ret = -1;
+ goto exit;
+ }
+
+#if defined(LWS_COOKIE_DEBUG)
+ char *po;
+ if (lws_cache_item_get(l1, cache_name, (const void **)&po, &size) ||
+ size != strlen(cookie_string) || memcmp(po, cookie_string, size)) {
+ lwsl_err("%s: L1 '%s' missing\n", __func__, cache_name);
+ }
+
+ if (lws_cache_item_get(wsi->a.context->nsc, cache_name,
+ (const void **)&po, &size) ||
+ size != strlen(cookie_string) ||
+ memcmp(po, cookie_string, size)) {
+ lwsl_err("%s: NSC '%s' missing, size %llu, po %s\n", __func__,
+ cache_name, (unsigned long long)size, po);
+ }
+#endif
+
+exit:
+ lws_free(cookie_string);
+
+ return ret;
+}
+
+static int
+lws_cookie_attach_cookies(struct lws *wsi, char *buf, char *end)
+{
+ const char *domain, *path, *dl_domain, *dl_path, *po;
+ char cache_name[LWS_COOKIE_MAX_CACHE_NAME_LEN];
+ size_t domain_len, path_len, size, ret = 0;
+ struct lws_cache_ttl_lru *l1;
+ struct client_info_stash *stash;
+ lws_cache_results_t cr;
+ struct lws_cookie c;
+ int hostdomain = 1;
+ char *p, *p1;
+
+ if (!wsi)
+ return -1;
+
+ stash = wsi->stash ? wsi->stash : lws_get_network_wsi(wsi)->stash;
+ if (!stash || !stash->cis[CIS_ADDRESS] ||
+ !stash->cis[CIS_PATH])
+ return -1;
+
+ l1 = wsi->a.context->l1;
+ if (!l1 || !wsi->a.context->nsc){
+ lwsl_err("%s:no cookiejar\n", __func__);
+ return -1;
+ }
+
+ memset(&c, 0, sizeof(c));
+
+ domain = stash->cis[CIS_ADDRESS];
+ path = stash->cis[CIS_PATH];
+
+ if (!domain || !path)
+ return -1;
+
+ path_len = strlen(path);
+
+ /* remove query string if exist */
+ dl_path = memchr(path, '?', path_len);
+ if (dl_path)
+ path_len = lws_ptr_diff_size_t(dl_path, path);
+
+ /* remove last slash if exist */
+ if (path_len != 1 && path[path_len - 1] == '/')
+ path_len--;
+
+ if (!path_len)
+ return -1;
+
+ lwsl_cookie("%s: path %.*s len %d\n", __func__, (int)path_len, path, (int)path_len);
+
+ /* when dest buf is not provided, we only return size of cookie string */
+ if (!buf || !end)
+ p = NULL;
+ else
+ p = buf;
+
+ /* iterate through domain and path levels to find matching cookies */
+ dl_domain = domain;
+ while (dl_domain) {
+ domain_len = strlen(domain);
+ dl_domain = memchr(domain, '.', domain_len);
+ /* don't match top level domain */
+ if (!dl_domain)
+ break;
+
+ if (domain_len + path_len + 6 > sizeof(cache_name))
+ return -1;
+
+ /* compile key string "[domain]|[path]|*"" */
+ p1 = cache_name;
+ memcpy(p1, domain, domain_len);
+ p1 += domain_len;
+ *p1 = '|';
+ p1++;
+ memcpy(p1, path, path_len);
+ p1 += path_len;
+ *p1 = '|';
+ p1++;
+ *p1 = '*';
+ p1++;
+ *p1 = '\0';
+
+ lwsl_cookie("%s: looking for %s\n", __func__, cache_name);
+
+ if (!lws_cache_lookup(l1, cache_name,
+ (const void **)&cr.ptr, &cr.size)) {
+
+ while (!lws_cache_results_walk(&cr)) {
+ lwsl_cookie(" %s (%d)\n", (const char *)cr.tag,
+ (int)cr.payload_len);
+
+ if (lws_cache_item_get(l1, (const char *)cr.tag,
+ (const void **)&po, &size) ||
+ lws_cookie_parse_nsc(&c, po, size)) {
+ lwsl_err("%s: failed to get c '%s'\n",
+ __func__, cr.tag);
+ break;
+ }
+
+ if (c.f[CE_HOSTONLY] && !hostdomain){
+ lwsl_cookie("%s: not sending this\n",
+ __func__);
+ continue;
+ }
+
+ if (p) {
+ if (ret) {
+ *p = ';';
+ p++;
+ *p = ' ';
+ p++;
+ }
+
+ memcpy(p, c.f[CE_NAME], c.l[CE_NAME]);
+ p += c.l[CE_NAME];
+ *p = '=';
+ p++;
+ memcpy(p, c.f[CE_VALUE], c.l[CE_VALUE]);
+ p += c.l[CE_VALUE];
+ }
+
+ if (ret)
+ ret += 2;
+ ret += c.l[CE_NAME] + 1 + c.l[CE_VALUE];
+
+ }
+ }
+
+ domain = dl_domain + 1;
+ hostdomain = 0;
+ }
+
+ lwsl_notice("%s: c len (%d)\n", __func__, (int)ret);
+
+ return (int)ret;
+}
+
+static struct {
+ const char *const name;
+ uint8_t len;
+} cft[] = {
+ { "domain=", 7 },
+ { "path=", 5 },
+ { "expires=", 8 },
+ { "max-age=", 8 },
+ { "httponly", 8 },
+ { "secure", 6 }
+};
+
+int
+lws_parse_set_cookie(struct lws *wsi)
+{
+ char *tk_head, *tk_end, *buf_head, *buf_end, *cookiep, *dl;
+ struct lws_cache_ttl_lru *l1;
+ struct lws_cookie c;
+ size_t fl;
+ int f, n;
+
+ if (!wsi)
+ return -1;
+
+ l1 = wsi->a.context->l1;
+ if (!l1)
+ return -1;
+
+ f = wsi->http.ah->frag_index[WSI_TOKEN_HTTP_SET_COOKIE];
+
+ while (f) {
+ cookiep = wsi->http.ah->data + wsi->http.ah->frags[f].offset;
+ fl = wsi->http.ah->frags[f].len;
+ f = wsi->http.ah->frags[f].nfrag;
+
+ if (!cookiep || !fl)
+ continue;
+
+#if defined(LWS_COOKIE_DEBUG)
+ lwsl_notice("%s:parsing: %.*s\n", __func__, (int)fl, cookiep);
+#endif
+
+ buf_head = cookiep;
+ buf_end = cookiep + fl - 1;
+ memset(&c, 0, sizeof(struct lws_cookie));
+
+ do {
+ tk_head = buf_head;
+ tk_end = memchr(buf_head, ';',
+ (size_t)(buf_end - buf_head + 1));
+ if (!tk_end) {
+ tk_end = buf_end;
+ buf_head = buf_end;
+ } else {
+ buf_head = tk_end + 1;
+ tk_end--;
+ }
+
+ if (c.f[CE_NAME])
+ goto parse_av;
+
+ /*
+ * find name value, remove leading trailing
+ * WS and DQ for value
+ */
+
+ dl = memchr(tk_head, '=', lws_ptr_diff_size_t(tk_end,
+ tk_head + 1));
+ if (!dl || dl == tk_head)
+ return -1;
+
+ c.f[CE_NAME] = tk_head;
+ c.l[CE_NAME] = lws_ptr_diff_size_t(dl, tk_head);
+ lws_cookie_rm_sws(&c.f[CE_NAME], &c.l[CE_NAME]);
+
+ if (!c.l[CE_NAME])
+ return -1;
+
+ lwsl_cookie("%s: c name l %d v:%.*s\n", __func__,
+ (int)c.l[CE_NAME],
+ (int)c.l[CE_NAME], c.f[CE_NAME]);
+ c.f[CE_VALUE] = dl + 1;
+ c.l[CE_VALUE] = lws_ptr_diff_size_t(tk_end,
+ c.f[CE_VALUE]) + 1;
+
+ lws_cookie_rm_sws(&c.f[CE_VALUE], &c.l[CE_VALUE]);
+ if (c.l[CE_VALUE] >= 2 && c.f[CE_VALUE][0] == '\"') {
+ c.f[CE_VALUE]++;
+ c.l[CE_VALUE] -= 2;
+ }
+ lwsl_cookie("%s: c value l %d v:%.*s\n", __func__,
+ (int)c.l[CE_VALUE], (int)c.l[CE_VALUE],
+ c.f[CE_VALUE]);
+ continue;
+
+parse_av:
+ while (*tk_head == ' ') {
+ if (tk_head == tk_end)
+ return -1;
+
+ tk_head++;
+ }
+
+ for (n = 0; n < (int)LWS_ARRAY_SIZE(cft); n++) {
+ if (lws_tolower(*tk_head) != cft[n].name[0])
+ continue;
+
+ if (!is_iprefix(tk_head,
+ lws_ptr_diff_size_t(tk_end,
+ tk_head) + 1,
+ cft[n].name, cft[n].len))
+ continue;
+
+ if (n == 4 || n == 5) {
+ c.f[n] = "T";
+ c.l[n] = 1;
+ break;
+ }
+
+ c.f[n] = tk_head + cft[n].len;
+ c.l[n] = lws_ptr_diff_size_t(tk_end, c.f[n]) + 1;
+ lws_cookie_rm_sws(&c.f[n], &c.l[n]);
+
+ if (n == CE_DOMAIN && c.l[0] &&
+ c.f[n][0] == '.'){
+ c.f[n]++;
+ c.l[n]--;
+ }
+
+ lwsl_cookie("%s: %s l %d v:%.*s\n", __func__,
+ cft[n].name, (int)c.l[n],
+ (int)c.l[n], c.f[n]);
+ break;
+ }
+
+ } while (tk_end != buf_end);
+
+ if (lws_cookie_write_nsc(wsi, &c))
+ lwsl_err("%s:failed to write nsc\n", __func__);
+ }
+
+ return 0;
+}
+
+int
+lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end)
+{
+ char *p;
+ int size;
+
+ if (!wsi || !pp || !(*pp) || !end)
+ return -1;
+
+ size = lws_cookie_attach_cookies(wsi, NULL, NULL);
+
+ if (!size)
+ return 0;
+ if (size < 0) {
+ lwsl_err("%s:failed to get cookie string size\n", __func__);
+ return -1;
+ }
+
+ lwsl_notice("%s: size %d\n", __func__, size);
+
+#if defined(LWS_COOKIE_DEBUG)
+ char *p_dbg = *pp;
+#endif
+
+ if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_COOKIE, NULL, size,
+ (unsigned char **)pp, (unsigned char *)end))
+ return -1;
+
+#if defined(LWS_COOKIE_DEBUG)
+ lwsl_notice("%s: dummy copy (%.*s) \n", __func__, (int)(*pp - p_dbg), p_dbg);
+#endif
+
+
+#ifdef LWS_WITH_HTTP2
+ if (lws_wsi_is_h2(wsi))
+ p = *pp - size;
+ else
+#endif
+ p = *pp - size - 2;
+
+ if (lws_cookie_attach_cookies(wsi, p, p + size) <= 0) {
+ lwsl_err("%s:failed to attach cookies\n", __func__);
+ return -1;
+ }
+
+#if defined(LWS_COOKIE_DEBUG)
+ lwsl_notice("%s: real copy (%.*s) total len %d\n", __func__, (int)(*pp - p_dbg), p_dbg, (int)(*pp - p_dbg));
+ lwsl_hexdump_notice(p_dbg, (size_t)(*pp - p_dbg));
+#endif
+
+ return 0;
+}
diff --git a/lib/roles/http/header.c b/lib/roles/http/header.c
index 83983a57..b0d54776 100644
--- a/lib/roles/http/header.c
+++ b/lib/roles/http/header.c
@@ -52,7 +52,7 @@ lws_http_string_to_known_header(const char *s, size_t slen)
}
#ifdef LWS_WITH_HTTP2
-static int
+int
lws_wsi_is_h2(struct lws *wsi)
{
return wsi->upgraded_to_http2 ||
@@ -87,7 +87,8 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
if (*p + length + 3 >= end)
return 1;
- memcpy(*p, value, (unsigned int)length);
+ if (value)
+ memcpy(*p, value, (unsigned int)length);
*p += length;
*((*p)++) = '\x0d';
*((*p)++) = '\x0a';