mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
http: cookies: support cookie jar in and out
This commit is contained in:
parent
b67d192100
commit
b31c5d6ffe
24 changed files with 951 additions and 32 deletions
|
@ -222,7 +222,7 @@
|
|||
"platforms": "not linux-centos-8/x86_64-amd/gcc"
|
||||
},
|
||||
"lwsws2": {
|
||||
"cmake": "-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_LWS_DSH=1",
|
||||
"cmake": "-DLWS_WITH_LWSWS=ON -DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_HTTP2=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=DEBUG -DLWS_WITH_LWS_DSH=1 -DLWS_WITH_CACHE_NSCOOKIEJAR=0",
|
||||
# no distro -devel package for libuv
|
||||
"platforms": "not linux-centos-8/x86_64-amd/gcc"
|
||||
},
|
||||
|
|
|
@ -278,7 +278,11 @@ option(LWS_WITH_SUL_DEBUGGING "Enable zombie lws_sul checking on object deletion
|
|||
option(LWS_WITH_PLUGINS_API "Build generic lws_plugins apis (see LWS_WITH_PLUGINS to also build protocol plugins)" OFF)
|
||||
option(LWS_WITH_CONMON "Collect introspectable connection latency stats on individual client connections" ON)
|
||||
option(LWS_WITHOUT_EVENTFD "Force using pipe instead of eventfd" OFF)
|
||||
option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" OFF)
|
||||
if (UNIX OR WIN32)
|
||||
option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" ON)
|
||||
else()
|
||||
option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" OFF)
|
||||
endif()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
option(LWS_WITH_NETLINK "Monitor Netlink for Routing Table changes" ON)
|
||||
|
|
40
READMEs/README.http-cache.md
Normal file
40
READMEs/README.http-cache.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Client http cookie storage, caching and application
|
||||
|
||||
lws now has the option to store incoming cookies in a Netscape cookie jar file
|
||||
persistently, and auto-apply relevant cookies to future outgoing requests.
|
||||
|
||||
A L1 heap cache of recent cookies is maintained, along with LRU tracking and
|
||||
removal of entries from cache and the cookie jar file according to their cookie
|
||||
expiry time.
|
||||
|
||||
The cookie handling is off by default per-connection for backwards compatibility
|
||||
and to avoid unexpected tracking.
|
||||
|
||||
## Enabling at build-time
|
||||
|
||||
Make sure `-DLWS_WITH_CACHE_NSCOOKIEJAR=1` is enabled at cmake (it is on by
|
||||
default now).
|
||||
|
||||
## Configuring the cookie cache
|
||||
|
||||
The cookie cache is managed through context creation info struct members.
|
||||
|
||||
|member|function|
|
||||
|---|---|
|
||||
|`.http_nsc_filepath`|Filepath to store the cookie jar file at|
|
||||
|`.http_nsc_heap_max_footprint`|0, or Max size in bytes for the L1 heap cache|
|
||||
|`.http_nsc_heap_max_items`|0, or Max number of cookies allowed in L1 heap cache|
|
||||
|`.http_nsc_heap_max_payload`|0, or Largest cookie we are willing to handle|
|
||||
|
||||
## Enabling per-connection in lws
|
||||
|
||||
To enable it on connections at lws level, add the flag `LCCSCF_CACHE_COOKIES` to
|
||||
the client connection info struct `.ssl_connection` flags.
|
||||
|
||||
## Enabling per-connection in Secure Streams policy
|
||||
|
||||
To enable it on Secure Streams, in the streamtype policy add
|
||||
|
||||
```
|
||||
"http_cookies": true
|
||||
```
|
|
@ -96,6 +96,9 @@ enum lws_client_connect_ssl_connection_flags {
|
|||
LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS = (1 << 29),
|
||||
/**< By default lws rejects https redirecting to http. Set this
|
||||
* flag on the client connection to allow it. */
|
||||
LCCSCF_CACHE_COOKIES = (1 << 30),
|
||||
/**< If built with -DLWS_WITH_CACHE_NSCOOKIEJAR, store and reapply
|
||||
* http cookies in a Netscape Cookie Jar on this connection */
|
||||
};
|
||||
|
||||
/** struct lws_client_connect_info - parameters to connect with when using
|
||||
|
|
|
@ -899,6 +899,21 @@ struct lws_context_creation_info {
|
|||
/**< CONTEXT: NULL to use the default, process-scope logging context,
|
||||
* else a specific logging context to associate with this context */
|
||||
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
|
||||
const char *http_nsc_filepath;
|
||||
/**< CONTEXT: Filepath to use for http netscape cookiejar file */
|
||||
|
||||
size_t http_nsc_heap_max_footprint;
|
||||
/**< CONTEXT: 0, or limit in bytes for heap usage of memory cookie
|
||||
* cache */
|
||||
size_t http_nsc_heap_max_items;
|
||||
/**< CONTEXT: 0, or the max number of items allowed in the cookie cache
|
||||
* before destroying lru items to keep it under the limit */
|
||||
size_t http_nsc_heap_max_payload;
|
||||
/**< CONTEXT: 0, or the maximum size of a single cookie we are able to
|
||||
* handle */
|
||||
#endif
|
||||
|
||||
/* Add new things just above here ---^
|
||||
* This is part of the ABI, don't needlessly break compatibility
|
||||
*
|
||||
|
|
|
@ -160,6 +160,8 @@ enum {
|
|||
/**< capture and report performace information */
|
||||
LWSSSPOLF_DIRECT_PROTO_STR = (1 << 23),
|
||||
/**< metadata as direct protocol string, e.g. http header */
|
||||
LWSSSPOLF_HTTP_CACHE_COOKIES = (1 << 24),
|
||||
/**< Record http cookies and pass them back on future requests */
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -386,6 +386,9 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
unsigned short count_threads = 1;
|
||||
uint8_t *u;
|
||||
uint16_t us_wait_resolution = 0;
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
|
||||
struct lws_cache_creation_info ci;
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
struct rlimit rt;
|
||||
|
@ -412,6 +415,7 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
char fatal_exit_defer = 0;
|
||||
#endif
|
||||
|
||||
|
||||
if (lws_fi(&info->fic, "ctx_createfail1"))
|
||||
goto early_bail;
|
||||
|
||||
|
@ -1374,6 +1378,34 @@ lws_create_context(const struct lws_context_creation_info *info)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
|
||||
if (info->http_nsc_filepath) {
|
||||
memset(&ci, 0, sizeof(ci));
|
||||
|
||||
ci.cx = context;
|
||||
ci.ops = &lws_cache_ops_nscookiejar;
|
||||
ci.name = "NSC";
|
||||
ci.u.nscookiejar.filepath = info->http_nsc_filepath;
|
||||
|
||||
context->nsc = lws_cache_create(&ci);
|
||||
if (!context->nsc)
|
||||
goto bail;
|
||||
|
||||
ci.ops = &lws_cache_ops_heap;
|
||||
ci.name = "L1";
|
||||
ci.parent = context->nsc;
|
||||
ci.max_footprint = info->http_nsc_heap_max_footprint;
|
||||
ci.max_items = info->http_nsc_heap_max_items;
|
||||
ci.max_payload = info->http_nsc_heap_max_payload;
|
||||
|
||||
context->l1 = lws_cache_create(&ci);
|
||||
if (!context->l1) {
|
||||
lwsl_err("Failed to init cookiejar");
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SECURE_STREAMS)
|
||||
|
||||
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
|
||||
|
@ -2080,6 +2112,11 @@ next:
|
|||
lws_tls_jit_trust_inflight_destroy_all(context);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
|
||||
lws_cache_destroy(&context->nsc);
|
||||
lws_cache_destroy(&context->l1);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SYS_SMD)
|
||||
_lws_smd_destroy(context);
|
||||
#endif
|
||||
|
|
|
@ -539,6 +539,9 @@ struct lws_context {
|
|||
/**< Toplevel Fault Injection ctx */
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
|
||||
struct lws_cache_ttl_lru *l1, *nsc;
|
||||
#endif
|
||||
|
||||
#if defined(LWS_WITH_SYS_NTPCLIENT)
|
||||
void *ntpclient_priv;
|
||||
|
@ -762,6 +765,13 @@ lws_jws_base64_enc(const char *in, size_t in_len, char *out, size_t out_max);
|
|||
void
|
||||
lws_vhost_destroy1(struct lws_vhost *vh);
|
||||
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT)
|
||||
int
|
||||
lws_parse_set_cookie(struct lws *wsi);
|
||||
|
||||
int
|
||||
lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end);
|
||||
#endif
|
||||
|
||||
#if defined(LWS_PLAT_FREERTOS)
|
||||
int
|
||||
|
@ -872,6 +882,9 @@ lws_vhost_protocol_options(struct lws_vhost *vh, const char *name);
|
|||
const struct lws_http_mount *
|
||||
lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len);
|
||||
|
||||
#ifdef LWS_WITH_HTTP2
|
||||
int lws_wsi_is_h2(struct lws *wsi);
|
||||
#endif
|
||||
/*
|
||||
* custom allocator
|
||||
*/
|
||||
|
|
|
@ -85,7 +85,7 @@ nsc_backing_open_lock(lws_cache_nscookiejar_t *cache, int mode, const char *par)
|
|||
|
||||
do {
|
||||
fd_lock = open(lock, LWS_O_CREAT | O_EXCL, 0600);
|
||||
if (fd_lock != LWS_INVALID_FILE) {
|
||||
if (fd_lock >= 0) {
|
||||
close(fd_lock);
|
||||
break;
|
||||
}
|
||||
|
@ -93,16 +93,20 @@ nsc_backing_open_lock(lws_cache_nscookiejar_t *cache, int mode, const char *par)
|
|||
if (!sanity--) {
|
||||
lwsl_warn("%s: unable to lock %s: errno %d\n", __func__,
|
||||
lock, errno);
|
||||
return LWS_INVALID_FILE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
Sleep(100);
|
||||
#else
|
||||
usleep(100000);
|
||||
#endif
|
||||
} while (1);
|
||||
|
||||
fd = open(cache->cache.info.u.nscookiejar.filepath,
|
||||
LWS_O_CREAT | mode, 0600);
|
||||
|
||||
if (fd == LWS_INVALID_FILE) {
|
||||
if (fd == -1) {
|
||||
lwsl_warn("%s: unable to open or create %s\n", __func__,
|
||||
cache->cache.info.u.nscookiejar.filepath);
|
||||
unlink(lock);
|
||||
|
@ -120,7 +124,8 @@ nsc_backing_close_unlock(lws_cache_nscookiejar_t *cache, int fd)
|
|||
|
||||
lws_snprintf(lock, sizeof(lock), "%s.LCK",
|
||||
cache->cache.info.u.nscookiejar.filepath);
|
||||
close(fd);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
unlink(lock);
|
||||
}
|
||||
|
||||
|
@ -148,7 +153,8 @@ nscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd,
|
|||
int m = 0, n = 0, e, r = LCN_SOL, ignore = 0, ret = 0;
|
||||
char temp[256], eof = 0;
|
||||
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
|
||||
return -1;
|
||||
|
||||
do { /* for as many buffers in the file */
|
||||
|
||||
|
@ -156,7 +162,8 @@ nscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd,
|
|||
|
||||
lwsl_debug("%s: n %d, m %d\n", __func__, n, m);
|
||||
|
||||
n1 = (int)read(fd, temp + m, sizeof(temp) - (size_t)m);
|
||||
read:
|
||||
n1 = (int)read(fd, temp + n, sizeof(temp) - (size_t)n);
|
||||
|
||||
lwsl_debug("%s: n1 %d\n", __func__, n1);
|
||||
|
||||
|
@ -198,6 +205,8 @@ nscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd,
|
|||
ret = e;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
goto read;
|
||||
}
|
||||
|
||||
if (m) {
|
||||
|
@ -480,12 +489,13 @@ lws_cache_nscookiejar_lookup(struct lws_cache_ttl_lru *_c,
|
|||
int ret, fd;
|
||||
|
||||
fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
|
||||
if (fd == LWS_INVALID_FILE)
|
||||
if (fd < 0)
|
||||
return 1;
|
||||
|
||||
ctx.wildcard_key = wildcard_key;
|
||||
ctx.results_owner = results_owner;
|
||||
ctx.wklen = strlen(wildcard_key);
|
||||
ctx.match = 0;
|
||||
|
||||
ret = nscookiejar_iterate(cache, fd, nsc_lookup_cb, &ctx);
|
||||
/*
|
||||
|
@ -548,7 +558,7 @@ nsc_regen_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
|
|||
(expiry && cache->earliest_expiry > expiry))
|
||||
cache->earliest_expiry = expiry;
|
||||
|
||||
if (expiry < ctx->curr)
|
||||
if (expiry && expiry < ctx->curr)
|
||||
/* routinely strip anything beyond its expiry */
|
||||
goto drop;
|
||||
|
||||
|
@ -570,7 +580,7 @@ nsc_regen_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
|
|||
|
||||
cache->cache.current_footprint += (uint64_t)size;
|
||||
|
||||
if ((size_t)write(ctx->fdt, buf, size) != size)
|
||||
if (write(ctx->fdt, buf, /*msvc*/(unsigned int)size) != (ssize_t)size)
|
||||
return NIR_FINISH_ERROR;
|
||||
|
||||
if (flags & LCN_EOL)
|
||||
|
@ -594,7 +604,7 @@ nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete,
|
|||
int fd, ret = 1;
|
||||
|
||||
fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
|
||||
if (fd == LWS_INVALID_FILE)
|
||||
if (fd < 0)
|
||||
return 1;
|
||||
|
||||
lws_snprintf(filepath, sizeof(filepath), "%s.tmp",
|
||||
|
@ -605,7 +615,7 @@ nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete,
|
|||
goto bail;
|
||||
|
||||
ctx.fdt = open(filepath, LWS_O_CREAT | LWS_O_WRONLY, 0600);
|
||||
if (ctx.fdt == LWS_INVALID_FILE)
|
||||
if (ctx.fdt < 0)
|
||||
goto bail;
|
||||
|
||||
/* magic header */
|
||||
|
@ -617,9 +627,11 @@ nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete,
|
|||
|
||||
/* if we are adding something, put it first */
|
||||
|
||||
if (pay && (size_t)write(ctx.fdt, pay, pay_size) != pay_size)
|
||||
if (pay &&
|
||||
write(ctx.fdt, pay, /*msvc*/(unsigned int)pay_size) !=
|
||||
(ssize_t)pay_size)
|
||||
goto bail1;
|
||||
if (pay && (size_t)write(ctx.fdt, "\n", 1) != 1)
|
||||
if (pay && write(ctx.fdt, "\n", 1u) != (ssize_t)1)
|
||||
goto bail1;
|
||||
|
||||
cache->cache.current_footprint = 0;
|
||||
|
@ -637,19 +649,25 @@ nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete,
|
|||
goto bail1;
|
||||
|
||||
close(ctx.fdt);
|
||||
ctx.fdt = -1;
|
||||
|
||||
unlink(cache->cache.info.u.nscookiejar.filepath);
|
||||
rename(filepath, cache->cache.info.u.nscookiejar.filepath);
|
||||
if (unlink(cache->cache.info.u.nscookiejar.filepath) == -1)
|
||||
lwsl_info("%s: unlink %s failed\n", __func__,
|
||||
cache->cache.info.u.nscookiejar.filepath);
|
||||
if (rename(filepath, cache->cache.info.u.nscookiejar.filepath) == -1)
|
||||
lwsl_info("%s: rename %s failed\n", __func__,
|
||||
cache->cache.info.u.nscookiejar.filepath);
|
||||
|
||||
if (cache->earliest_expiry)
|
||||
lws_cache_schedule(&cache->cache, expiry_cb,
|
||||
cache->earliest_expiry);
|
||||
|
||||
ret = 0;
|
||||
goto bail1;
|
||||
goto bail;
|
||||
|
||||
bail1:
|
||||
close(ctx.fdt);
|
||||
if (ctx.fdt >= 0)
|
||||
close(ctx.fdt);
|
||||
bail:
|
||||
unlink(filepath);
|
||||
|
||||
|
@ -815,7 +833,7 @@ lws_cache_nscookiejar_get(struct lws_cache_ttl_lru *_c,
|
|||
int ret, fd;
|
||||
|
||||
fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
|
||||
if (fd == LWS_INVALID_FILE)
|
||||
if (fd < 0)
|
||||
return 1;
|
||||
|
||||
/* get a pointer to l1 */
|
||||
|
@ -915,7 +933,7 @@ lws_cache_nscookiejar_debug_dump(struct lws_cache_ttl_lru *_c)
|
|||
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
|
||||
int fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
|
||||
|
||||
if (fd == LWS_INVALID_FILE)
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
lwsl_cache("%s: %s\n", __func__, _c->info.name);
|
||||
|
|
|
@ -356,8 +356,7 @@ lws_cache_heap_write(struct lws_cache_ttl_lru *_c, const char *specific_key,
|
|||
* matching rules at the backing store level
|
||||
*/
|
||||
|
||||
if (!backing->info.ops->tag_match(backing, iname + 1,
|
||||
specific_key, 1))
|
||||
if (!strcmp(iname + 1, specific_key))
|
||||
_lws_cache_heap_item_destroy(cache, i);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)));
|
||||
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);
|
||||
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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
729
lib/roles/http/cookie.c
Normal file
729
lib/roles/http/cookie.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -621,6 +621,10 @@ The `content-type` to mark up the multipart mime section with if present
|
|||
|
||||
Indicate the data is sent in `x-www-form-urlencoded` form
|
||||
|
||||
### `http_cookies`
|
||||
|
||||
This streamtype should store and bring out http cookies from the peer.
|
||||
|
||||
### `rideshare`
|
||||
|
||||
For special cases where one logically separate stream travels with another when using this
|
||||
|
|
|
@ -98,6 +98,7 @@ static const char * const lejp_tokens_policy[] = {
|
|||
"s[].*.http_mime_content_type",
|
||||
"s[].*.http_www_form_urlencoded",
|
||||
"s[].*.http_expect",
|
||||
"s[].*.http_cookies",
|
||||
"s[].*.http_fail_redirect",
|
||||
"s[].*.http_multipart_ss_in",
|
||||
"s[].*.ws_subprotocol",
|
||||
|
@ -199,6 +200,7 @@ typedef enum {
|
|||
LSSPPT_HTTP_MULTIPART_CONTENT_TYPE,
|
||||
LSSPPT_HTTP_WWW_FORM_URLENCODED,
|
||||
LSSPPT_HTTP_EXPECT,
|
||||
LSSPPT_HTTP_COOKIES,
|
||||
LSSPPT_HTTP_FAIL_REDIRECT,
|
||||
LSSPPT_HTTP_MULTIPART_SS_IN,
|
||||
LSSPPT_WS_SUBPROTOCOL,
|
||||
|
@ -771,6 +773,11 @@ lws_ss_policy_parser_cb(struct lejp_ctx *ctx, char reason)
|
|||
a->curr[LTY_POLICY].p->flags |=
|
||||
LWSSSPOLF_ALLOW_REDIRECTS;
|
||||
break;
|
||||
case LSSPPT_HTTP_COOKIES:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |=
|
||||
LWSSSPOLF_HTTP_CACHE_COOKIES;
|
||||
break;
|
||||
case LSSPPT_HTTP_MULTIPART_SS_IN:
|
||||
if (reason == LEJPCB_VAL_TRUE)
|
||||
a->curr[LTY_POLICY].p->flags |=
|
||||
|
|
|
@ -1149,6 +1149,9 @@ secstream_connect_munge_h1(lws_ss_handle_t *h, char *buf, size_t len,
|
|||
i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
|
||||
#endif
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES)
|
||||
i->ssl_connection |= LCCSCF_CACHE_COOKIES;
|
||||
|
||||
/* protocol aux is the path part */
|
||||
|
||||
i->path = buf;
|
||||
|
|
|
@ -158,6 +158,9 @@ secstream_connect_munge_h2(lws_ss_handle_t *h, char *buf, size_t len,
|
|||
if (h->policy->flags & LWSSSPOLF_HTTP_X_WWW_FORM_URLENCODED)
|
||||
i->ssl_connection |= LCCSCF_HTTP_X_WWW_FORM_URLENCODED;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES)
|
||||
i->ssl_connection |= LCCSCF_CACHE_COOKIES;
|
||||
|
||||
i->ssl_connection |= LCCSCF_PIPELINE;
|
||||
|
||||
i->alpn = "h2";
|
||||
|
|
|
@ -222,6 +222,9 @@ secstream_connect_munge_ws(lws_ss_handle_t *h, char *buf, size_t len,
|
|||
if (!pbasis)
|
||||
return 0;
|
||||
|
||||
if (h->policy->flags & LWSSSPOLF_HTTP_CACHE_COOKIES)
|
||||
i->ssl_connection |= LCCSCF_CACHE_COOKIES;
|
||||
|
||||
/* protocol aux is the path part ; ws subprotocol name */
|
||||
|
||||
i->path = buf;
|
||||
|
|
|
@ -170,7 +170,7 @@ static int
|
|||
test_nsc1(void)
|
||||
{
|
||||
struct lws_cache_creation_info ci;
|
||||
struct lws_cache_ttl_lru *l1, *nsc;
|
||||
struct lws_cache_ttl_lru *l1 = NULL, *nsc;
|
||||
lws_cache_results_t cr;
|
||||
int n, ret = 1;
|
||||
size_t size;
|
||||
|
|
|
@ -21,6 +21,8 @@ Commandline option|Meaning
|
|||
-j|Apply tls option LCCSCF_ALLOW_SELFSIGNED
|
||||
-m|Apply tls option LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK
|
||||
-e|Apply tls option LCCSCF_ALLOW_EXPIRED
|
||||
-b|Apply tls option LCCSCF_CACHE_COOKIES
|
||||
-c <cookie jar file>|Set filepath used for cookie jar
|
||||
-v|Connection validity use 3s / 10s instead of default 5m / 5m10s
|
||||
--nossl| disable ssl connection
|
||||
--user <username>| Set Basic Auth username
|
||||
|
|
|
@ -279,6 +279,9 @@ system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link,
|
|||
if (lws_cmdline_option(a->argc, a->argv, "-k"))
|
||||
i.ssl_connection |= LCCSCF_ALLOW_INSECURE;
|
||||
|
||||
if (lws_cmdline_option(a->argc, a->argv, "-b"))
|
||||
i.ssl_connection |= LCCSCF_CACHE_COOKIES;
|
||||
|
||||
if (lws_cmdline_option(a->argc, a->argv, "-m"))
|
||||
i.ssl_connection |= LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
|
||||
|
||||
|
@ -362,6 +365,12 @@ int main(int argc, const char **argv)
|
|||
info.register_notifier_list = na;
|
||||
info.connect_timeout_secs = 30;
|
||||
|
||||
#if defined(LWS_WITH_CACHE_NSCOOKIEJAR)
|
||||
info.http_nsc_filepath = "./cookies.txt";
|
||||
if ((p = lws_cmdline_option(argc, argv, "-c")))
|
||||
info.http_nsc_filepath = p;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* since we know this lws context is only ever going to be used with
|
||||
* one client wsis / fds / sockets at a time, let lws know it doesn't
|
||||
|
|
Loading…
Add table
Reference in a new issue