serving: protect file sending from downgrading to waiting pipelined headers
[platform/upstream/libwebsockets.git] / lib / handshake.c
index 98eec31..8172e96 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2015 Andy Green <andy@warmcat.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
 
 #include "private-libwebsockets.h"
 
-#define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); }
-#define LWS_CPYAPP_TOKEN(ptr, tok) { strcpy(p, wsi->utf8_token[tok].token); \
-               p += wsi->utf8_token[tok].token_len; }
-
-static int
-interpret_key(const char *key, unsigned long *result)
-{
-       char digits[20];
-       int digit_pos = 0;
-       const char *p = key;
-       unsigned int spaces = 0;
-       unsigned long acc = 0;
-       int rem = 0;
-
-       while (*p) {
-               if (!isdigit(*p)) {
-                       p++;
-                       continue;
-               }
-               if (digit_pos == sizeof(digits) - 1)
-                       return -1;
-               digits[digit_pos++] = *p++;
-       }
-       digits[digit_pos] = '\0';
-       if (!digit_pos)
-               return -2;
-
-       while (*key) {
-               if (*key == ' ')
-                       spaces++;
-               key++;
-       }
-
-       if (!spaces)
-               return -3;
-
-       p = &digits[0];
-       while (*p) {
-               rem = (rem * 10) + ((*p++) - '0');
-               acc = (acc * 10) + (rem / spaces);
-               rem -= (rem / spaces) * spaces;
-       }
-
-       if (rem) {
-               lwsl_warn("nonzero handshake remainder\n");
-               return -1;
-       }
-
-       *result = acc;
-
-       return 0;
-}
-
-
-static int
-handshake_00(struct libwebsocket_context *context, struct libwebsocket *wsi)
-{
-       unsigned long key1, key2;
-       unsigned char sum[16];
-       char *response;
-       char *p;
-       int n;
-
-       /* Confirm we have all the necessary pieces */
-
-       if (!wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len ||
-               !wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
-               !wsi->utf8_token[WSI_TOKEN_CHALLENGE].token_len ||
-               !wsi->utf8_token[WSI_TOKEN_KEY1].token_len ||
-                            !wsi->utf8_token[WSI_TOKEN_KEY2].token_len)
-               /* completed header processing, but missing some bits */
-               goto bail;
-
-       /* allocate the per-connection user memory (if any) */
-       if (wsi->protocol->per_session_data_size &&
-                                         !libwebsocket_ensure_user_space(wsi))
-               goto bail;
-
-       /* create the response packet */
-
-       /* make a buffer big enough for everything */
-
-       response = (char *)malloc(256 +
-               wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
-               wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
-               wsi->utf8_token[WSI_TOKEN_HOST].token_len +
-               wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len +
-               wsi->utf8_token[WSI_TOKEN_GET_URI].token_len +
-               wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
-       if (!response) {
-               lwsl_err("Out of memory for response buffer\n");
-               goto bail;
-       }
-
-       p = response;
-       LWS_CPYAPP(p, "HTTP/1.1 101 WebSocket Protocol Handshake\x0d\x0a"
-                     "Upgrade: WebSocket\x0d\x0a"
-                     "Connection: Upgrade\x0d\x0a"
-                     "Sec-WebSocket-Origin: ");
-       strcpy(p, wsi->utf8_token[WSI_TOKEN_ORIGIN].token);
-       p += wsi->utf8_token[WSI_TOKEN_ORIGIN].token_len;
-#ifdef LWS_OPENSSL_SUPPORT
-       if (wsi->ssl) {
-               LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Location: wss://");
-       } else {
-               LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Location: ws://");
-       }
-#else
-       LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Location: ws://");
-#endif
-
-       LWS_CPYAPP_TOKEN(p, WSI_TOKEN_HOST);
-       LWS_CPYAPP_TOKEN(p, WSI_TOKEN_GET_URI);
-
-       if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
-               LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
-               LWS_CPYAPP_TOKEN(p, WSI_TOKEN_PROTOCOL);
-       }
-
-       LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
-
-       /* convert the two keys into 32-bit integers */
-
-       if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY1].token, &key1))
-               goto bail;
-       if (interpret_key(wsi->utf8_token[WSI_TOKEN_KEY2].token, &key2))
-               goto bail;
-
-       /* lay them out in network byte order (MSB first */
-
-       sum[0] = (unsigned char)(key1 >> 24);
-       sum[1] = (unsigned char)(key1 >> 16);
-       sum[2] = (unsigned char)(key1 >> 8);
-       sum[3] = (unsigned char)(key1);
-       sum[4] = (unsigned char)(key2 >> 24);
-       sum[5] = (unsigned char)(key2 >> 16);
-       sum[6] = (unsigned char)(key2 >> 8);
-       sum[7] = (unsigned char)(key2);
-
-       /* follow them with the challenge token we were sent */
-
-       memcpy(&sum[8], wsi->utf8_token[WSI_TOKEN_CHALLENGE].token, 8);
-
-       /*
-        * compute the md5sum of that 16-byte series and use as our
-        * payload after our headers
-        */
-
-       MD5(sum, 16, (unsigned char *)p);
-       p += 16;
-
-       /* it's complete: go ahead and send it */
-
-       lwsl_parser("issuing response packet %d len\n", (int)(p - response));
-#ifdef _DEBUG
-       fwrite(response, 1,  p - response, stderr);
-#endif
-       n = libwebsocket_write(wsi, (unsigned char *)response,
-                                         p - response, LWS_WRITE_HTTP);
-       if (n < 0) {
-               lwsl_debug("ERROR writing to socket");
-               goto bail;
-       }
-
-       /* alright clean up and set ourselves into established state */
-
-       free(response);
-       wsi->state = WSI_STATE_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(wsi->protocol->owning_server,
-                               wsi, LWS_CALLBACK_ESTABLISHED,
-                                         wsi->user_space, NULL, 0);
-
-       return 0;
-
-bail:
-       return -1;
-}
-
-/*
- * Perform the newer BASE64-encoded handshake scheme
- */
-
-static int
-handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
-{
-       static const char *websocket_magic_guid_04 =
-                                        "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
-       static const char *websocket_magic_guid_04_masking =
-                                        "61AC5F19-FBBA-4540-B96F-6561F1AB40A8";
-       char accept_buf[MAX_WEBSOCKET_04_KEY_LEN + 37];
-       char nonce_buf[256];
-       char mask_summing_buf[256 + MAX_WEBSOCKET_04_KEY_LEN + 37];
-       unsigned char hash[20];
-       int n;
-       char *response;
-       char *p;
-       char *m = mask_summing_buf;
-       int nonce_len = 0;
-       int accept_len;
-       char *c;
-       char ext_name[128];
-       struct libwebsocket_extension *ext;
-       int ext_count = 0;
-       int more = 1;
-
-       if (!wsi->utf8_token[WSI_TOKEN_HOST].token_len ||
-           !wsi->utf8_token[WSI_TOKEN_KEY].token_len) {
-               lwsl_parser("handshake_04 missing pieces\n");
-               /* completed header processing, but missing some bits */
-               goto bail;
-       }
-
-       if (wsi->utf8_token[WSI_TOKEN_KEY].token_len >=
-                                                    MAX_WEBSOCKET_04_KEY_LEN) {
-               lwsl_warn("Client sent handshake key longer "
-                          "than max supported %d\n", MAX_WEBSOCKET_04_KEY_LEN);
-               goto bail;
-       }
-
-       strcpy(accept_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
-       strcpy(accept_buf + wsi->utf8_token[WSI_TOKEN_KEY].token_len,
-                                                      websocket_magic_guid_04);
-
-       SHA1((unsigned char *)accept_buf,
-                       wsi->utf8_token[WSI_TOKEN_KEY].token_len +
-                                        strlen(websocket_magic_guid_04), hash);
-
-       accept_len = lws_b64_encode_string((char *)hash, 20, accept_buf,
-                                                            sizeof accept_buf);
-       if (accept_len < 0) {
-               lwsl_warn("Base64 encoded hash too long\n");
-               goto bail;
-       }
-
-       /* allocate the per-connection user memory (if any) */
-       if (wsi->protocol->per_session_data_size &&
-                                         !libwebsocket_ensure_user_space(wsi))
-               goto bail;
-
-       /* create the response packet */
-
-       /* make a buffer big enough for everything */
-
-       response = (char *)malloc(256 +
-               wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len +
-               wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len +
-               wsi->utf8_token[WSI_TOKEN_PROTOCOL].token_len);
-       if (!response) {
-               lwsl_err("Out of memory for response buffer\n");
-               goto bail;
-       }
-
-       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, accept_buf);
-       p += accept_len;
-
-       if (wsi->ietf_spec_revision == 4) {
-               LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Nonce: ");
-
-               /* select the nonce */
-
-               n = libwebsockets_get_random(wsi->protocol->owning_server,
-                                                                     hash, 16);
-               if (n != 16) {
-                       lwsl_err("Unable to read random device %s %d\n",
-                                                    SYSTEM_RANDOM_FILEPATH, n);
-                       if (wsi->user_space)
-                               free(wsi->user_space);
-                       goto bail;
-               }
-
-               /* encode the nonce */
-
-               nonce_len = lws_b64_encode_string((const char *)hash, 16,
-                                                  nonce_buf, sizeof nonce_buf);
-               if (nonce_len < 0) {
-                       lwsl_err("Failed to base 64 encode the nonce\n");
-                       if (wsi->user_space)
-                               free(wsi->user_space);
-                       goto bail;
-               }
-
-               /* apply the nonce */
-
-               strcpy(p, nonce_buf);
-               p += nonce_len;
-       }
-
-       if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token) {
-               LWS_CPYAPP(p, "\x0d\x0aSec-WebSocket-Protocol: ");
-               LWS_CPYAPP_TOKEN(p, WSI_TOKEN_PROTOCOL);
-       }
-
-       /*
-        * Figure out which extensions the client has that we want to
-        * enable on this connection, and give him back the list
-        */
-
-       if (wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token_len) {
-
-               /*
-                * break down the list of client extensions
-                * and go through them
-                */
-
-               c = wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token;
-               lwsl_parser("wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token = %s\n",
-                                 wsi->utf8_token[WSI_TOKEN_EXTENSIONS].token);
-               wsi->count_active_extensions = 0;
-               n = 0;
-               while (more) {
-
-                       if (*c && (*c != ',' && *c != ' ' && *c != '\t')) {
-                               ext_name[n] = *c++;
-                               if (n < sizeof(ext_name) - 1)
-                                       n++;
-                               continue;
-                       }
-                       ext_name[n] = '\0';
-                       if (!*c)
-                               more = 0;
-                       else {
-                               c++;
-                               if (!n)
-                                       continue;
-                       }
-
-                       /* check a client's extension against our support */
-
-                       ext = wsi->protocol->owning_server->extensions;
-
-                       while (ext && ext->callback) {
-
-                               if (strcmp(ext_name, ext->name)) {
-                                       ext++;
-                                       continue;
-                               }
-
-                               /*
-                                * oh, we do support this one he
-                                * asked for... but let's ask user
-                                * code if it's OK to apply it on this
-                                * particular connection + protocol
-                                */
-
-                               n = wsi->protocol->owning_server->
-                                       protocols[0].callback(
-                                               wsi->protocol->owning_server,
-                                               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) {
-                                       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] =
-                                            malloc(ext->per_session_data_size);
-                               if (wsi->active_extensions_user[
-                                        wsi->count_active_extensions] == NULL) {
-                                       lwsl_err("Out of mem\n");
-                                       free(response);
-                                       goto bail;
-                               }
-                               memset(wsi->active_extensions_user[
-                                       wsi->count_active_extensions], 0,
-                                                   ext->per_session_data_size);
-
-                               wsi->active_extensions[
-                                         wsi->count_active_extensions] = ext;
-
-                               /* allow him to construct his context */
-
-                               ext->callback(wsi->protocol->owning_server,
-                                               ext, wsi,
-                                               LWS_EXT_CALLBACK_CONSTRUCT,
-                                               wsi->active_extensions_user[
-                                       wsi->count_active_extensions], NULL, 0);
-
-                               wsi->count_active_extensions++;
-                               lwsl_parser("wsi->count_active_extensions <- %d",
-                                                 wsi->count_active_extensions);
-
-                               ext++;
-                       }
-
-                       n = 0;
-               }
-       }
-
-       /* end of response packet */
-
-       LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a");
-
-       if (wsi->ietf_spec_revision == 4) {
-
-               /*
-                * precompute the masking key the client will use from the SHA1
-                * hash of ( base 64 client key we were sent, concatenated with
-                * the bse 64 nonce we sent, concatenated with a magic constant
-                * guid specified by the 04 standard )
-                *
-                * We store the hash in the connection's wsi ready to use with
-                * undoing the masking the client has done on framed data it
-                * sends (we send our data to the client in clear).
-                */
-
-               strcpy(mask_summing_buf, wsi->utf8_token[WSI_TOKEN_KEY].token);
-               m += wsi->utf8_token[WSI_TOKEN_KEY].token_len;
-               strcpy(m, nonce_buf);
-               m += nonce_len;
-               strcpy(m, websocket_magic_guid_04_masking);
-               m += strlen(websocket_magic_guid_04_masking);
-
-               SHA1((unsigned char *)mask_summing_buf, m - mask_summing_buf,
-                                                          wsi->masking_key_04);
-       }
-
-       if (!lws_any_extension_handled(context, wsi,
-                       LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX,
-                                                    response, p - response)) {
-
-               /* okay send the handshake response accepting the connection */
-
-               lwsl_parser("issuing response packet %d len\n", (int)(p - response));
-       #ifdef DEBUG
-               fwrite(response, 1,  p - response, stderr);
-       #endif
-               n = libwebsocket_write(wsi, (unsigned char *)response,
-                                                 p - response, LWS_WRITE_HTTP);
-               if (n < 0) {
-                       lwsl_debug("ERROR writing to socket");
-                       goto bail;
-               }
-
-       }
-
-       /* alright clean up and set ourselves into established state */
-
-       free(response);
-       wsi->state = WSI_STATE_ESTABLISHED;
-       wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-       wsi->rx_packet_length = 0;
-
-       /* notify user code that we're ready to roll */
-
-       if (wsi->protocol->callback)
-               wsi->protocol->callback(wsi->protocol->owning_server,
-                               wsi, LWS_CALLBACK_ESTABLISHED,
-                                         wsi->user_space, NULL, 0);
-
-       return 0;
-
-
-bail:
-       return -1;
-}
-
-
 /*
  * -04 of the protocol (actually the 80th version) has a radically different
  * handshake.  The 04 spec gives the following idea
@@ -535,196 +46,216 @@ bail:
  *       Sec-WebSocket-Protocol: chat
  */
 
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
 /*
  * We have to take care about parsing because the headers may be split
  * into multiple fragments.  They may contain unknown headers with arbitrary
  * argument lengths.  So, we parse using a single-character at a time state
  * machine that is completely independent of packet size.
+ *
+ * Returns <0 for error or length of chars consumed from buf (up to len)
  */
 
-int
-libwebsocket_read(struct libwebsocket_context *context,
-                    struct libwebsocket *wsi, unsigned char * buf, size_t len)
+LWS_VISIBLE int
+lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 {
+       unsigned char *last_char, *oldbuf = buf;
+       lws_filepos_t body_chunk_len;
        size_t n;
 
-       switch (wsi->state) {
-       case WSI_STATE_HTTP:
-               wsi->state = WSI_STATE_HTTP_HEADERS;
-               wsi->parser_state = WSI_TOKEN_NAME_PART;
-               /* fallthru */
-       case WSI_STATE_HTTP_HEADERS:
-
-               lwsl_parser("issuing %d bytes to parser\n", (int)len);
-#ifdef _DEBUG
-               //fwrite(buf, 1, len, stderr);
-#endif
-
-               switch (wsi->mode) {
-               case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY:
-               case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE:
-               case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY:
-               case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT:
-               case LWS_CONNMODE_WS_CLIENT:
-                       for (n = 0; n < len; n++)
-                               libwebsocket_client_rx_sm(wsi, *buf++);
-
-                       return 0;
-               default:
-                       break;
-               }
-
-               /* LWS_CONNMODE_WS_SERVING */
-
-               for (n = 0; n < len; n++)
-                       libwebsocket_parse(wsi, *buf++);
-
-               if (wsi->parser_state != WSI_PARSING_COMPLETE)
-                       break;
-
-               lwsl_parser("seem to be serving, mode is %d\n", wsi->mode);
-
-               lwsl_parser("libwebsocket_parse sees parsing complete\n");
+       lwsl_debug("%s: incoming len %d  state %d\n", __func__, (int)len, wsi->state);
 
-               /* is this websocket protocol or normal http 1.0? */
+       switch (wsi->state) {
+#ifdef LWS_USE_HTTP2
+       case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
+       case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
+       case LWSS_HTTP2_ESTABLISHED:
+               n = 0;
+               while (n < len) {
+                       /*
+                        * we were accepting input but now we stopped doing so
+                        */
+                       if (!(wsi->rxflow_change_to & LWS_RXFLOW_ALLOW)) {
+                               lws_rxflow_cache(wsi, buf, n, len);
+
+                               return 1;
+                       }
 
-               if (!wsi->utf8_token[WSI_TOKEN_UPGRADE].token_len ||
-                            !wsi->utf8_token[WSI_TOKEN_CONNECTION].token_len) {
-                       wsi->state = WSI_STATE_HTTP;
-                       if (wsi->protocol->callback)
-                               if (wsi->protocol->callback(context, wsi,
-                                                               LWS_CALLBACK_HTTP, wsi->user_space,
-                                                               wsi->utf8_token[WSI_TOKEN_GET_URI].token,
-                                                               wsi->utf8_token[WSI_TOKEN_GET_URI].token_len))
-                                       goto bail;
-                       return 0;
+                       /* account for what we're using in rxflow buffer */
+                       if (wsi->rxflow_buffer)
+                               wsi->rxflow_pos++;
+                       if (lws_http2_parser(wsi, buf[n++])) {
+                               lwsl_debug("%s: http2_parser bailed\n", __func__);
+                               goto bail;
+                       }
                }
+               break;
+#endif
 
-               if (!wsi->protocol)
-                       lwsl_err("NULL protocol at libwebsocket_read\n");
-
-               /*
-                * It's websocket
-                *
-                * Make sure user side is happy about protocol
-                */
+       case LWSS_HTTP_ISSUING_FILE:
+               return 0;
 
-               while (wsi->protocol->callback) {
+       case LWSS_CLIENT_HTTP_ESTABLISHED:
+               break;
 
-                       if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL) {
-                               if (wsi->protocol->name == NULL)
-                                       break;
-                       } else
-                               if (wsi->protocol->name && strcmp(
-                                    wsi->utf8_token[WSI_TOKEN_PROTOCOL].token,
-                                                     wsi->protocol->name) == 0)
-                                       break;
+       case LWSS_HTTP:
+               wsi->hdr_parsing_completed = 0;
+               /* fallthru */
 
-                       wsi->protocol++;
+       case LWSS_HTTP_HEADERS:
+               if (!wsi->u.hdr.ah) {
+                       lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
+                       assert(0);
                }
+               lwsl_parser("issuing %d bytes to parser\n", (int)len);
 
-               /* we didn't find a protocol he wanted? */
-
-               if (wsi->protocol->callback == NULL) {
-                       if (wsi->utf8_token[WSI_TOKEN_PROTOCOL].token == NULL)
-                               lwsl_err("[no protocol] "
-                                       "not supported (use NULL .name)\n");
-                       else
-                               lwsl_err("Requested protocol %s "
-                                               "not supported\n",
-                                    wsi->utf8_token[WSI_TOKEN_PROTOCOL].token);
+               if (lws_handshake_client(wsi, &buf, (size_t)len))
                        goto bail;
-               }
 
-               /*
-                * find out which spec version the client is using
-                * if this header is not given, we default to 00 (aka 76)
-                */
+               last_char = buf;
+               if (lws_handshake_server(wsi, &buf, (size_t)len))
+                       /* Handshake indicates this session is done. */
+                       goto bail;
 
-               if (wsi->utf8_token[WSI_TOKEN_VERSION].token_len)
-                       wsi->ietf_spec_revision =
-                                atoi(wsi->utf8_token[WSI_TOKEN_VERSION].token);
+               /* we might have transitioned to RAW */
+               if (wsi->mode == LWSCM_RAW)
+                        /* we gave the read buffer to RAW handler already */
+                       goto read_ok;
 
                /*
-                * Give the user code a chance to study the request and
-                * have the opportunity to deny it
+                * It's possible that we've exhausted our data already, or
+                * rx flow control has stopped us dealing with this early,
+                * but lws_handshake_server doesn't update len for us.
+                * Figure out how much was read, so that we can proceed
+                * appropriately:
                 */
-
-               if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi,
-                               LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION,
-                                               &wsi->utf8_token[0], NULL, 0)) {
-                       lwsl_warn("User code denied connection\n");
-                       goto bail;
+               len -= (buf - last_char);
+               lwsl_debug("%s: thinks we have used %ld\n", __func__, (long)len);
+
+               if (!wsi->hdr_parsing_completed)
+                       /* More header content on the way */
+                       goto read_ok;
+
+               switch (wsi->state) {
+                       case LWSS_HTTP:
+                       case LWSS_HTTP_HEADERS:
+                               goto read_ok;
+                       case LWSS_HTTP_ISSUING_FILE:
+                               goto read_ok;
+                       case LWSS_HTTP_BODY:
+                               wsi->u.http.content_remain =
+                                               wsi->u.http.content_length;
+                               if (wsi->u.http.content_remain)
+                                       goto http_postbody;
+
+                               /* there is no POST content */
+                               goto postbody_completion;
+                       default:
+                               break;
                }
+               break;
 
+       case LWSS_HTTP_BODY:
+http_postbody:
+               while (len && wsi->u.http.content_remain) {
+                       /* Copy as much as possible, up to the limit of:
+                        * what we have in the read buffer (len)
+                        * remaining portion of the POST body (content_remain)
+                        */
+                       body_chunk_len = min(wsi->u.http.content_remain,len);
+                       wsi->u.http.content_remain -= body_chunk_len;
+                       len -= body_chunk_len;
+#ifdef LWS_WITH_CGI
+                       if (wsi->cgi) {
+                               struct lws_cgi_args args;
+
+                               args.ch = LWS_STDIN;
+                               args.stdwsi = &wsi->cgi->stdwsi[0];
+                               args.data = buf;
+                               args.len = body_chunk_len;
+
+                               /* returns how much used */
+                               n = user_callback_handle_rxflow(
+                                       wsi->protocol->callback,
+                                       wsi, LWS_CALLBACK_CGI_STDIN_DATA,
+                                       wsi->user_space,
+                                       (void *)&args, 0);
+                               if ((int)n < 0)
+                                       goto bail;
+                       } else {
+#endif
+                               n = wsi->protocol->callback(wsi,
+                                       LWS_CALLBACK_HTTP_BODY, wsi->user_space,
+                                       buf, (size_t)body_chunk_len);
+                               if (n)
+                                       goto bail;
+                               n = (size_t)body_chunk_len;
+#ifdef LWS_WITH_CGI
+                       }
+#endif
+                       buf += n;
 
-               /*
-                * Perform the handshake according to the protocol version the
-                * client announced
-                */
+                       if (wsi->u.http.content_remain)  {
+                               lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
+                                               wsi->context->timeout_secs);
+                               break;
+                       }
+                       /* he sent all the content in time */
+postbody_completion:
+#ifdef LWS_WITH_CGI
+                       /* if we're running a cgi, we can't let him off the hook just because he sent his POST data */
+                       if (wsi->cgi)
+                               lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, wsi->context->timeout_secs);
+                       else
+#endif
+                       lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
+#ifdef LWS_WITH_CGI
+                       if (!wsi->cgi)
+#endif
+                       {
+                               n = wsi->protocol->callback(wsi,
+                                       LWS_CALLBACK_HTTP_BODY_COMPLETION,
+                                       wsi->user_space, NULL, 0);
+                               if (n)
+                                       goto bail;
+                       }
 
-               switch (wsi->ietf_spec_revision) {
-               case 0: /* applies to 76 and 00 */
-                       wsi->xor_mask = xor_no_mask;
-                       if (handshake_00(context, wsi))
-                               goto bail;
-                       break;
-               case 4: /* 04 */
-                       wsi->xor_mask = xor_mask_04;
-                       lwsl_parser("libwebsocket_parse calling handshake_04\n");
-                       if (handshake_0405(context, wsi))
-                               goto bail;
-                       break;
-               case 5:
-               case 6:
-               case 7:
-               case 8:
-               case 13:
-                       wsi->xor_mask = xor_mask_05;
-                       lwsl_parser("libwebsocket_parse calling handshake_04\n");
-                       if (handshake_0405(context, wsi))
-                               goto bail;
                        break;
-
-               default:
-                       lwsl_warn("Unknown client spec version %d\n",
-                                                      wsi->ietf_spec_revision);
-                       goto bail;
                }
-
-               lwsl_parser("accepted v%02d connection\n",
-                                                      wsi->ietf_spec_revision);
-
                break;
 
-       case WSI_STATE_AWAITING_CLOSE_ACK:
-       case WSI_STATE_ESTABLISHED:
+       case LWSS_ESTABLISHED:
+       case LWSS_AWAITING_CLOSE_ACK:
+       case LWSS_SHUTDOWN:
+               if (lws_handshake_client(wsi, &buf, (size_t)len))
+                       goto bail;
                switch (wsi->mode) {
-               case LWS_CONNMODE_WS_CLIENT:
-                       for (n = 0; n < len; n++)
-                               if (libwebsocket_client_rx_sm(wsi, *buf++) < 0)
-                                       goto bail;
+               case LWSCM_WS_SERVING:
 
-                       return 0;
-               default:
+                       if (lws_interpret_incoming_packet(wsi, &buf, (size_t)len) < 0) {
+                               lwsl_info("interpret_incoming_packet has bailed\n");
+                               goto bail;
+                       }
                        break;
                }
-
-               /* LWS_CONNMODE_WS_SERVING */
-
-               if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0)
-                       goto bail;
-
                break;
        default:
+               lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
                break;
        }
 
-       return 0;
+read_ok:
+       /* Nothing more to do for now */
+       lwsl_info("%s: read_ok, used %ld\n", __func__, (long)(buf - oldbuf));
+
+       return buf - oldbuf;
 
 bail:
-       libwebsocket_close_and_free_session(context, wsi,
-                                                    LWS_CLOSE_STATUS_NOSTATUS);
+       //lwsl_notice("closing connection at lws_read bail:\n");
+       lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
 
        return -1;
 }