summaryrefslogtreecommitdiff
path: root/thirdparty/lws/ext/extension.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/lws/ext/extension.c')
-rw-r--r--thirdparty/lws/ext/extension.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/thirdparty/lws/ext/extension.c b/thirdparty/lws/ext/extension.c
new file mode 100644
index 0000000000..ac28204034
--- /dev/null
+++ b/thirdparty/lws/ext/extension.c
@@ -0,0 +1,344 @@
+#include "private-libwebsockets.h"
+
+#include "extension-permessage-deflate.h"
+
+LWS_VISIBLE void
+lws_context_init_extensions(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
+};
+
+LWS_VISIBLE 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) {
+ 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 = 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;
+
+ for (n = 0; n < wsi->count_act_ext; n++) {
+ m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
+ wsi->active_extensions[n], wsi, reason,
+ wsi->act_ext_user[n], arg, len);
+ if (m < 0) {
+ lwsl_ext("Ext '%s' failed to handle callback %d!\n",
+ wsi->active_extensions[n]->name, reason);
+ return -1;
+ }
+ /* valgrind... */
+ if (reason == LWS_EXT_CB_DESTROY)
+ wsi->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)
+ return 0;
+
+ ext = wsi->vhost->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->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 eff_buf;
+ int ret, m, n = 0;
+
+ eff_buf.token = (char *)buf;
+ eff_buf.token_len = 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, &eff_buf, 0);
+ if (m < 0)
+ return -1;
+ if (m) /* handled */
+ ret = 1;
+
+ if ((char *)buf != eff_buf.token)
+ /*
+ * extension recreated it:
+ * need to buffer this if not all sent
+ */
+ wsi->u.ws.clean_buffer = 0;
+
+ /* assuming they left us something to send, send it */
+
+ if (eff_buf.token_len) {
+ n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
+ eff_buf.token_len);
+ if (n < 0) {
+ lwsl_info("closing from ext access\n");
+ return -1;
+ }
+
+ /* always either sent it all or privately buffered */
+ if (wsi->u.ws.clean_buffer)
+ len = n;
+ }
+
+ lwsl_parser("written %d bytes to client\n", n);
+
+ /* no extension has more to spill? Then we can go */
+
+ if (!ret)
+ break;
+
+ /* we used up what we had */
+
+ eff_buf.token = NULL;
+ eff_buf.token_len = 0;
+
+ /*
+ * Did that leave the pipe choked?
+ * Or we had to hold on to some of it?
+ */
+
+ if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
+ /* 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->extension_data_pending = 1;
+ ret = 0;
+ }
+
+ return 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;
+
+ /* maybe an extension will take care of it for us */
+
+ for (n = 0; n < wsi->count_act_ext && !handled; n++) {
+ if (!wsi->active_extensions[n]->callback)
+ continue;
+
+ handled |= wsi->active_extensions[n]->callback(context,
+ wsi->active_extensions[n], wsi,
+ r, wsi->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;
+
+ /* first identify if the ext is active on this wsi */
+ while (idx < wsi->count_act_ext &&
+ strcmp(wsi->active_extensions[idx]->name, ext_name))
+ idx++;
+
+ if (idx == wsi->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->active_extensions[idx]->callback(
+ wsi->context, wsi->active_extensions[idx], wsi,
+ LWS_EXT_CB_NAMED_OPTION_SET, wsi->act_ext_user[idx], &oa, 0);
+}