You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

384 lines
8.4 KiB

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "extension-permessage-deflate.h"
void
lws_context_init_extensions(const struct lws_context_creation_info *info,
struct lws_context *context)
{
lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
}
enum lws_ext_option_parser_states {
LEAPS_SEEK_NAME,
LEAPS_EAT_NAME,
LEAPS_SEEK_VAL,
LEAPS_EAT_DEC,
LEAPS_SEEK_ARG_TERM
};
int
lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
void *ext_user, const struct lws_ext_options *opts,
const char *in, int len)
{
enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
unsigned int match_map = 0, n, m, w = 0, count_options = 0,
pending_close_quote = 0;
struct lws_ext_option_arg oa;
oa.option_name = NULL;
while (opts[count_options].name)
count_options++;
while (len) {
lwsl_ext("'%c' %d", *in, leap);
switch (leap) {
case LEAPS_SEEK_NAME:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
match_map = (1 << count_options) - 1;
leap = LEAPS_EAT_NAME;
w = 0;
/* fallthru */
case LEAPS_EAT_NAME:
oa.start = NULL;
oa.len = 0;
m = match_map;
n = 0;
pending_close_quote = 0;
while (m) {
if (!(m & 1)) {
m >>= 1;
n++;
continue;
}
lwsl_ext(" m=%d, n=%d, w=%d\n", m, n, w);
if (*in == opts[n].name[w]) {
if (!opts[n].name[w + 1]) {
oa.option_index = n;
lwsl_ext("hit %d\n",
oa.option_index);
leap = LEAPS_SEEK_VAL;
if (len == 1)
goto set_arg;
break;
}
} else {
match_map &= ~(1 << n);
if (!match_map) {
lwsl_ext("empty match map\n");
return -1;
}
}
m >>= 1;
n++;
}
w++;
break;
case LEAPS_SEEK_VAL:
if (*in == ' ')
break;
if (*in == ',') {
len = 1;
break;
}
if (*in == ';' || len == 1) { /* ie,nonoptional */
if (opts[oa.option_index].type == EXTARG_DEC)
return -1;
leap = LEAPS_SEEK_NAME;
goto set_arg;
}
if (*in == '=') {
w = 0;
pending_close_quote = 0;
if (opts[oa.option_index].type == EXTARG_NONE)
return -1;
leap = LEAPS_EAT_DEC;
break;
}
return -1;
case LEAPS_EAT_DEC:
if (*in >= '0' && *in <= '9') {
if (!w)
oa.start = in;
w++;
if (len != 1)
break;
}
if (!w && *in =='"') {
pending_close_quote = 1;
break;
}
if (!w)
return -1;
if (pending_close_quote && *in != '"' && len != 1)
return -1;
leap = LEAPS_SEEK_ARG_TERM;
if (oa.start)
oa.len = lws_ptr_diff(in, oa.start);
if (len == 1)
oa.len++;
set_arg:
ext->callback(lws_get_context(wsi),
ext, wsi, LWS_EXT_CB_OPTION_SET,
ext_user, (char *)&oa, 0);
if (len == 1)
break;
if (pending_close_quote && *in == '"')
break;
/* fallthru */
case LEAPS_SEEK_ARG_TERM:
if (*in == ' ')
break;
if (*in == ';') {
leap = LEAPS_SEEK_NAME;
break;
}
if (*in == ',') {
len = 1;
break;
}
return -1;
}
len--;
in++;
}
return 0;
}
/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
{
int n, m, handled = 0;
if (!wsi->ws)
return 0;
for (n = 0; n < wsi->ws->count_act_ext; n++) {
m = wsi->ws->active_extensions[n]->callback(
lws_get_context(wsi), wsi->ws->active_extensions[n],
wsi, reason, wsi->ws->act_ext_user[n], arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->ws->active_extensions[n]->name, reason);
return -1;
}
/* valgrind... */
if (reason == LWS_EXT_CB_DESTROY)
wsi->ws->act_ext_user[n] = NULL;
if (m > handled)
handled = m;
}
return handled;
}
int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
int reason, void *arg, int len)
{
int n = 0, m, handled = 0;
const struct lws_extension *ext;
if (!wsi || !wsi->vhost || !wsi->ws)
return 0;
ext = wsi->vhost->ws.extensions;
while (ext && ext->callback && !handled) {
m = ext->callback(context, ext, wsi, reason,
(void *)(lws_intptr_t)n, arg, len);
if (m < 0) {
lwsl_ext("Ext '%s' failed to handle callback %d!\n",
wsi->ws->active_extensions[n]->name, reason);
return -1;
}
if (m)
handled = 1;
ext++;
n++;
}
return 0;
}
int
lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_tokens ebuf;
int ret, m, n = 0;
ebuf.token = buf;
ebuf.len = (int)len;
/*
* while we have original buf to spill ourselves, or extensions report
* more in their pipeline
*/
ret = 1;
while (ret == 1) {
/* default to nobody has more to spill */
ret = 0;
/* show every extension the new incoming data */
m = lws_ext_cb_active(wsi, LWS_EXT_CB_PACKET_TX_PRESEND,
&ebuf, 0);
if (m < 0)
return -1;
if (m) /* handled */
ret = 1;
if (buf != ebuf.token)
/*
* extension recreated it:
* need to buffer this if not all sent
*/
wsi->ws->clean_buffer = 0;
/* assuming they left us something to send, send it */
if (ebuf.len) {
n = lws_issue_raw(wsi, ebuf.token, ebuf.len);
if (n < 0) {
lwsl_info("closing from ext access\n");
return -1;
}
/* always either sent it all or privately buffered */
if (wsi->ws->clean_buffer)
len = n;
lwsl_ext("%s: written %d bytes to client\n",
__func__, n);
}
/* no extension has more to spill? Then we can go */
if (!ret)
break;
/* we used up what we had */
ebuf.token = NULL;
ebuf.len = 0;
/*
* Did that leave the pipe choked?
* Or we had to hold on to some of it?
*/
if (!lws_send_pipe_choked(wsi) && !lws_has_buffered_out(wsi))
/* no we could add more, lets's do that */
continue;
lwsl_debug("choked\n");
/*
* Yes, he's choked. Don't spill the rest now get a callback
* when he is ready to send and take care of it there
*/
lws_callback_on_writable(wsi);
wsi->ws->extension_data_pending = 1;
ret = 0;
}
return (int)len;
}
int
lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
void *v, size_t len)
{
struct lws_context *context = wsi->context;
int n, handled = 0;
if (!wsi->ws)
return 0;
/* maybe an extension will take care of it for us */
for (n = 0; n < wsi->ws->count_act_ext && !handled; n++) {
if (!wsi->ws->active_extensions[n]->callback)
continue;
handled |= wsi->ws->active_extensions[n]->callback(context,
wsi->ws->active_extensions[n], wsi,
r, wsi->ws->act_ext_user[n], v, len);
}
return handled;
}
int
lws_set_extension_option(struct lws *wsi, const char *ext_name,
const char *opt_name, const char *opt_val)
{
struct lws_ext_option_arg oa;
int idx = 0;
if (!wsi->ws)
return 0;
/* first identify if the ext is active on this wsi */
while (idx < wsi->ws->count_act_ext &&
strcmp(wsi->ws->active_extensions[idx]->name, ext_name))
idx++;
if (idx == wsi->ws->count_act_ext)
return -1; /* request ext not active on this wsi */
oa.option_name = opt_name;
oa.option_index = 0;
oa.start = opt_val;
oa.len = 0;
return wsi->ws->active_extensions[idx]->callback(wsi->context,
wsi->ws->active_extensions[idx], wsi,
LWS_EXT_CB_NAMED_OPTION_SET, wsi->ws->act_ext_user[idx],
&oa, 0);
}