clean: fixes for appveyor warnings
[platform/upstream/libwebsockets.git] / lib / server-handshake.c
index 9fd8cb2..ec9b14e 100644 (file)
 #include "private-libwebsockets.h"
 
 #define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
+
 #ifndef LWS_NO_EXTENSIONS
-LWS_VISIBLE int
-lws_extension_server_handshake(struct lws *wsi, char **p)
+static int
+lws_extension_server_handshake(struct lws *wsi, char **p, int budget)
 {
-       int n;
-       char *c;
-       char ext_name[128];
-       const struct lws_extension *ext;
        struct lws_context *context = wsi->context;
+       struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
+       char ext_name[64], *args, *end = (*p) + budget - 1;
+       const struct lws_ext_options *opts, *po;
+       const struct lws_extension *ext;
+       struct lws_ext_option_arg oa;
+       int n, m, more = 1;
        int ext_count = 0;
-       int more = 1;
+       char ignore;
+       char *c;
 
        /*
         * Figure out which extensions the client has that we want to
         * enable on this connection, and give him back the list
         */
-
        if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS))
                return 0;
 
@@ -47,24 +50,48 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
         * and go through them
         */
 
-       if (lws_hdr_copy(wsi, (char *)context->service_buffer,
-                       sizeof(context->service_buffer),
-                                             WSI_TOKEN_EXTENSIONS) < 0)
+       if (lws_hdr_copy(wsi, (char *)pt->serv_buf, context->pt_serv_buf_size,
+                        WSI_TOKEN_EXTENSIONS) < 0)
                return 1;
 
-       c = (char *)context->service_buffer;
+       c = (char *)pt->serv_buf;
        lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c);
-       wsi->count_active_extensions = 0;
+       wsi->count_act_ext = 0;
+       ignore = 0;
        n = 0;
+       args = NULL;
+
+       /*
+        * We may get a simple request
+        *
+        * Sec-WebSocket-Extensions: permessage-deflate
+        *
+        * or an elaborated one with requested options
+        *
+        * Sec-WebSocket-Extensions: permessage-deflate; \
+        *                           server_no_context_takeover; \
+        *                           client_no_context_takeover
+        */
+
        while (more) {
 
-               if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
+               if (*c && (*c != ',' && *c != '\t')) {
+                       if (*c == ';') {
+                               ignore = 1;
+                               args = c + 1;
+                       }
+                       if (ignore || *c == ' ') {
+                               c++;
+                               continue;
+                       }
                        ext_name[n] = *c++;
                        if (n < sizeof(ext_name) - 1)
                                n++;
                        continue;
                }
                ext_name[n] = '\0';
+
+               ignore = 0;
                if (!*c)
                        more = 0;
                else {
@@ -73,9 +100,12 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
                                continue;
                }
 
+               while (args && *args && *args == ' ')
+                       args++;
+
                /* check a client's extension against our support */
 
-               ext = lws_get_ctx(wsi)->extensions;
+               ext = wsi->vhost->extensions;
 
                while (ext && ext->callback) {
 
@@ -86,68 +116,109 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
 
                        /*
                         * oh, we do support this one he asked for... but let's
+                        * confirm he only gave it once
+                        */
+                       for (m = 0; m < wsi->count_act_ext; m++)
+                               if (wsi->active_extensions[m] == ext) {
+                                       lwsl_info("extension mentioned twice\n");
+                                       return 1; /* shenanigans */
+                               }
+
+                       /*
                         * ask user code if it's OK to apply it on this
                         * particular connection + protocol
                         */
-
-                       n = lws_get_ctx(wsi)->
-                               protocols[0].callback(
-                                       lws_get_ctx(wsi),
-                                       wsi,
-                                 LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
-                                         wsi->user_space, ext_name, 0);
+                       m = (wsi->protocol->callback)(wsi,
+                               LWS_CALLBACK_CONFIRM_EXTENSION_OKAY,
+                               wsi->user_space, ext_name, 0);
 
                        /*
                         * zero return from callback means go ahead and allow
                         * the extension, it's what we get if the callback is
                         * unhandled
                         */
-
-                       if (n) {
+                       if (m) {
                                ext++;
                                continue;
                        }
 
                        /* apply it */
 
-                       if (ext_count)
-                               *(*p)++ = ',';
-                       else
-                               LWS_CPYAPP(*p,
-                                "\x0d\x0aSec-WebSocket-Extensions: ");
-                       *p += sprintf(*p, "%s", ext_name);
                        ext_count++;
 
                        /* instantiate the extension on this conn */
 
-                       wsi->active_extensions_user[
-                               wsi->count_active_extensions] =
-                                    lws_zalloc(ext->per_session_data_size);
-                       if (wsi->active_extensions_user[
-                            wsi->count_active_extensions] == NULL) {
-                               lwsl_err("Out of mem\n");
-                               return 1;
+                       wsi->active_extensions[wsi->count_act_ext] = ext;
+
+                       /* allow him to construct his context */
+
+                       if (ext->callback(lws_get_context(wsi), ext, wsi,
+                                         LWS_EXT_CB_CONSTRUCT,
+                                         (void *)&wsi->act_ext_user[
+                                                           wsi->count_act_ext],
+                                         (void *)&opts, 0)) {
+                               lwsl_notice("ext %s failed construction\n",
+                                           ext_name);
+                               ext_count--;
+                               ext++;
+
+                               continue;
                        }
 
-                       wsi->active_extensions[
-                                 wsi->count_active_extensions] = ext;
+                       if (ext_count > 1)
+                               *(*p)++ = ',';
+                       else
+                               LWS_CPYAPP(*p,
+                                         "\x0d\x0aSec-WebSocket-Extensions: ");
+                       *p += lws_snprintf(*p, (end - *p), "%s", ext_name);
 
-                       /* allow him to construct his context */
+                       /*
+                        *  go through the options trying to apply the
+                        * recognized ones
+                        */
 
-                       ext->callback(lws_get_ctx(wsi),
-                                       ext, wsi,
-                                       LWS_EXT_CALLBACK_CONSTRUCT,
-                                       wsi->active_extensions_user[
-                               wsi->count_active_extensions], NULL, 0);
+                       lwsl_debug("ext args %s", args);
+
+                       while (args && *args && *args != ',') {
+                               while (*args == ' ')
+                                       args++;
+                               po = opts;
+                               while (po->name) {
+                                       lwsl_debug("'%s' '%s'\n", po->name, args);
+                                       /* only support arg-less options... */
+                                       if (po->type == EXTARG_NONE &&
+                                           !strncmp(args, po->name,
+                                                           strlen(po->name))) {
+                                               oa.option_name = NULL;
+                                               oa.option_index = po - opts;
+                                               oa.start = NULL;
+                                               lwsl_debug("setting %s\n", po->name);
+                                               if (!ext->callback(
+                                                               lws_get_context(wsi), ext, wsi,
+                                                                 LWS_EXT_CB_OPTION_SET,
+                                                                 wsi->act_ext_user[
+                                                                        wsi->count_act_ext],
+                                                                 &oa, (end - *p))) {
+
+                                                       *p += lws_snprintf(*p, (end - *p), "; %s", po->name);
+                                                       lwsl_debug("adding option %s\n", po->name);
+                                               }
+                                       }
+                                       po++;
+                               }
+                               while (*args && *args != ',' && *args != ';')
+                                       args++;
+                       }
 
-                       wsi->count_active_extensions++;
-                       lwsl_parser("count_active_extensions <- %d\n",
-                                         wsi->count_active_extensions);
+                       wsi->count_act_ext++;
+                       lwsl_parser("count_act_ext <- %d\n",
+                                   wsi->count_act_ext);
 
                        ext++;
                }
 
                n = 0;
+               args = NULL;
        }
 
        return 0;
@@ -156,21 +227,20 @@ lws_extension_server_handshake(struct lws *wsi, char **p)
 int
 handshake_0405(struct lws_context *context, struct lws *wsi)
 {
+       struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
        unsigned char hash[20];
-       int n;
+       int n, accept_len;
        char *response;
        char *p;
-       int accept_len;
 
        if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) ||
-                               !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
+           !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) {
                lwsl_parser("handshake_04 missing pieces\n");
                /* completed header processing, but missing some bits */
                goto bail;
        }
 
-       if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >=
-                                                    MAX_WEBSOCKET_04_KEY_LEN) {
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_KEY) >= MAX_WEBSOCKET_04_KEY_LEN) {
                lwsl_warn("Client key too long %d\n", MAX_WEBSOCKET_04_KEY_LEN);
                goto bail;
        }
@@ -179,15 +249,14 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
         * since key length is restricted above (currently 128), cannot
         * overflow
         */
-       n = sprintf((char *)context->service_buffer,
-                               "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
-                               lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
+       n = sprintf((char *)pt->serv_buf,
+                   "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
+                   lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY));
 
-       lws_SHA1(context->service_buffer, n, hash);
+       lws_SHA1(pt->serv_buf, n, hash);
 
        accept_len = lws_b64_encode_string((char *)hash, 20,
-                       (char *)context->service_buffer,
-                       sizeof(context->service_buffer));
+                       (char *)pt->serv_buf, context->pt_serv_buf_size);
        if (accept_len < 0) {
                lwsl_warn("Base64 encoded hash too long\n");
                goto bail;
@@ -201,29 +270,33 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
 
        /* make a buffer big enough for everything */
 
-       response = (char *)context->service_buffer + MAX_WEBSOCKET_04_KEY_LEN + LWS_SEND_BUFFER_PRE_PADDING;
+       response = (char *)pt->serv_buf + MAX_WEBSOCKET_04_KEY_LEN + LWS_PRE;
        p = response;
        LWS_CPYAPP(p, "HTTP/1.1 101 Switching Protocols\x0d\x0a"
                      "Upgrade: WebSocket\x0d\x0a"
                      "Connection: Upgrade\x0d\x0a"
                      "Sec-WebSocket-Accept: ");
-       strcpy(p, (char *)context->service_buffer);
+       strcpy(p, (char *)pt->serv_buf);
        p += accept_len;
 
-       if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) {
+       /* we can only return the protocol header if:
+        *  - one came in, and ... */
+       if (lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL) &&
+           /*  - it is not an empty string */
+           wsi->protocol->name &&
+           wsi->protocol->name[0]) {
                LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
-               n = lws_hdr_copy(wsi, p, 128, WSI_TOKEN_PROTOCOL);
-               if (n < 0)
-                       goto bail;
-               p += n;
+               p += lws_snprintf(p, 128, "%s", wsi->protocol->name);
        }
 
 #ifndef LWS_NO_EXTENSIONS
        /*
         * Figure out which extensions the client has that we want to
-        * enable on this connection, and give him back the list
+        * enable on this connection, and give him back the list.
+        *
+        * Give him a limited write bugdet
         */
-       if (lws_extension_server_handshake(wsi, &p))
+       if (lws_extension_server_handshake(wsi, &p, 192))
                goto bail;
 #endif
 
@@ -233,13 +306,13 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
 
        LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
 
-       if (!lws_any_extension_handled(wsi, LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
+       if (!lws_any_extension_handled(wsi, LWS_EXT_CB_HANDSHAKE_REPLY_TX,
                                       response, p - response)) {
 
                /* okay send the handshake response accepting the connection */
 
                lwsl_parser("issuing resp pkt %d len\n", (int)(p - response));
-#ifdef DEBUG
+#if defined(DEBUG) && ! defined(LWS_WITH_ESP8266)
                fwrite(response, 1,  p - response, stderr);
 #endif
                n = lws_write(wsi, (unsigned char *)response,
@@ -253,28 +326,26 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
 
        /* alright clean up and set ourselves into established state */
 
-       wsi->state = WSI_STATE_ESTABLISHED;
+       wsi->state = LWSS_ESTABLISHED;
        wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 
-       /* notify user code that we're ready to roll */
-
-       if (wsi->protocol->callback)
-               wsi->protocol->callback(lws_get_ctx(wsi),
-                               wsi, LWS_CALLBACK_ESTABLISHED,
-                                         wsi->user_space,
-#ifdef LWS_OPENSSL_SUPPORT
-                                         wsi->ssl,
-#else
-                                         NULL,
-#endif
-                                         0);
+       {
+               const char * uri_ptr =
+                       lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI);
+               int uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI);
+               const struct lws_http_mount *hit =
+                       lws_find_mount(wsi, uri_ptr, uri_len);
+               if (hit && hit->cgienv &&
+                   wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP_PMO,
+                       wsi->user_space, (void *)hit->cgienv, 0))
+                       return 1;
+       }
 
        return 0;
 
 
 bail:
-       /* free up his parsing allocations */
-       lws_free_header_table(wsi);
+       /* caller will free up his parsing allocations */
        return -1;
 }