diff options
author | Yichen Gu <guyiche@amazon.com> | 2021-07-05 16:41:41 +0800 |
---|---|---|
committer | Andy Green <andy@warmcat.com> | 2021-08-21 17:44:40 +0100 |
commit | b31c5d6ffe0b80811f3348a8a51b508d757c0f32 (patch) | |
tree | c41a5b65b9fca9b6e9914f755631a4b483a02229 /lib/roles | |
parent | b67d192100c37568d81099ad9aea2264b342e0ae (diff) | |
download | libwebsockets-b31c5d6ffe0b80811f3348a8a51b508d757c0f32.tar.gz |
http: cookies: support cookie jar in and out
Diffstat (limited to 'lib/roles')
-rw-r--r-- | lib/roles/h2/hpack.c | 17 | ||||
-rw-r--r-- | lib/roles/h2/http2.c | 5 | ||||
-rw-r--r-- | lib/roles/http/CMakeLists.txt | 7 | ||||
-rw-r--r-- | lib/roles/http/client/client-http.c | 12 | ||||
-rw-r--r-- | lib/roles/http/cookie.c | 729 | ||||
-rw-r--r-- | lib/roles/http/header.c | 5 |
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'; |