defeat POLLOUT if socket in shutdown wait
[platform/upstream/libwebsockets.git] / lib / libwebsockets.c
index 82f317a..b4bac9f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2016 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
@@ -52,33 +52,88 @@ lws_free_wsi(struct lws *wsi)
 
        lws_free_set_NULL(wsi->rxflow_buffer);
        lws_free_set_NULL(wsi->trunc_alloc);
-       /*
-        * These union members have an ah at the start
-        *
-        *      struct _lws_http_mode_related http;
-        *      struct _lws_http2_related http2;
-        *      struct _lws_header_related hdr;
-        *
-        * basically ws-related union member does not
-        */
-       if (wsi->mode != LWSCM_WS_CLIENT &&
-           wsi->mode != LWSCM_WS_SERVING)
-               if (wsi->u.hdr.ah)
-                       lws_free_header_table(wsi);
+
+       if (wsi->u.hdr.ah) {
+               /* we're closing, losing some rx is OK */
+               wsi->u.hdr.ah->rxpos = wsi->u.hdr.ah->rxlen;
+               lws_header_table_detach(wsi);
+       }
+
+       wsi->context->count_wsi_allocated--;
+       lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
+                       wsi->context->count_wsi_allocated);
+
        lws_free(wsi);
 }
 
+static void
+lws_remove_from_timeout_list(struct lws *wsi)
+{
+       struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+
+       if (!wsi->timeout_list_prev)
+               return;
+
+       lws_pt_lock(pt);
+       if (wsi->timeout_list)
+               wsi->timeout_list->timeout_list_prev = wsi->timeout_list_prev;
+       *wsi->timeout_list_prev = wsi->timeout_list;
+
+       wsi->timeout_list_prev = NULL;
+       wsi->timeout_list = NULL;
+       lws_pt_unlock(pt);
+}
+
+/**
+ * lws_set_timeout() - marks the wsi as subject to a timeout
+ *
+ * You will not need this unless you are doing something special
+ *
+ * @wsi:       Websocket connection instance
+ * @reason:    timeout reason
+ * @secs:      how many seconds
+ */
+
+LWS_VISIBLE void
+lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
+{
+       struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
+       time_t now;
+
+       lws_pt_lock(pt);
+
+       time(&now);
+
+       if (!wsi->pending_timeout && reason) {
+               wsi->timeout_list = pt->timeout_list;
+               if (wsi->timeout_list)
+                       wsi->timeout_list->timeout_list_prev = &wsi->timeout_list;
+               wsi->timeout_list_prev = &pt->timeout_list;
+               *wsi->timeout_list_prev = wsi;
+       }
+
+       wsi->pending_timeout_limit = now + secs;
+       wsi->pending_timeout = reason;
+
+       lws_pt_unlock(pt);
+
+       if (!reason)
+               lws_remove_from_timeout_list(wsi);
+}
+
 void
 lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 {
-       struct lws_context *context = wsi->context;
-       int n, m, ret, old_state;
+       struct lws_context *context;
+       struct lws_context_per_thread *pt;
+       int n, m, ret;
        struct lws_tokens eff_buf;
 
        if (!wsi)
                return;
 
-       old_state = wsi->state;
+       context = wsi->context;
+       pt = &context->pt[(int)wsi->tsi];
 
        if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED &&
            wsi->u.http.fd != LWS_INVALID_FILE) {
@@ -89,10 +144,13 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
                                               wsi->user_space, NULL, 0);
        }
        if (wsi->socket_is_permanently_unusable ||
-           reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)
+           reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
+           wsi->state == LWSS_SHUTDOWN)
                goto just_kill_connection;
 
-       switch (old_state) {
+       wsi->state_pre_close = wsi->state;
+
+       switch (wsi->state_pre_close) {
        case LWSS_DEAD_SOCKET:
                return;
 
@@ -130,8 +188,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
         * parent and just his ch1 aspect is closing?
         */
 
-       if (lws_ext_cb_wsi_active_exts(wsi,
-                     LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
+       if (lws_ext_cb_active(wsi,
+                     LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) {
                lwsl_ext("extension vetoed close\n");
                return;
        }
@@ -148,8 +206,8 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 
                /* show every extension the new incoming data */
 
-               m = lws_ext_cb_wsi_active_exts(wsi,
-                         LWS_EXT_CALLBACK_FLUSH_PENDING_TX, &eff_buf, 0);
+               m = lws_ext_cb_active(wsi,
+                         LWS_EXT_CB_FLUSH_PENDING_TX, &eff_buf, 0);
                if (m < 0) {
                        lwsl_ext("Extension reports fatal error\n");
                        goto just_kill_connection;
@@ -184,7 +242,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
         * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
         */
 
-       if (old_state == LWSS_ESTABLISHED &&
+       if (wsi->state_pre_close == LWSS_ESTABLISHED &&
            (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
             (reason != LWS_CLOSE_STATUS_NOSTATUS &&
             (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
@@ -193,14 +251,13 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
                /* if no prepared close reason, use 1000 and no aux data */
                if (!wsi->u.ws.close_in_ping_buffer_len) {
                        wsi->u.ws.close_in_ping_buffer_len = 2;
-                       wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING] =
+                       wsi->u.ws.ping_payload_buf[LWS_PRE] =
                                (reason >> 16) & 0xff;
-                       wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING + 1] =
+                       wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
                                reason & 0xff;
                }
 
-               n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[
-                                               LWS_SEND_BUFFER_PRE_PADDING],
+               n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
                              wsi->u.ws.close_in_ping_buffer_len,
                              LWS_WRITE_CLOSE);
                if (n >= 0) {
@@ -227,13 +284,38 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 
 just_kill_connection:
 
-       lwsl_debug("close: just_kill_connection\n");
+#if LWS_POSIX
+       /*
+        * Testing with ab shows that we have to stage the socket close when
+        * the system is under stress... shutdown any further TX, change the
+        * state to one that won't emit anything more, and wait with a timeout
+        * for the POLLIN to show a zero-size rx before coming back and doing
+        * the actual close.
+        */
+       if (wsi->state != LWSS_SHUTDOWN &&
+           reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
+           !wsi->socket_is_permanently_unusable) {
+               lwsl_info("%s: shutting down connection: %p\n", __func__, wsi);
+               n = shutdown(wsi->sock, SHUT_WR);
+               if (n)
+                       lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
+
+               lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
+               wsi->state = LWSS_SHUTDOWN;
+               lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
+                               context->timeout_secs);
+               return;
+       }
+#endif
+
+       lwsl_info("%s: real just_kill_connection: %p\n", __func__, wsi);
 
        /*
         * we won't be servicing or receiving anything further from this guy
         * delete socket from the internal poll list if still present
         */
        lws_ssl_remove_wsi_from_buffered_list(wsi);
+       lws_remove_from_timeout_list(wsi);
 
        /* checking return redundant since we anyway close */
        remove_wsi_socket_from_fds(wsi);
@@ -242,11 +324,40 @@ just_kill_connection:
 
        lws_free_set_NULL(wsi->rxflow_buffer);
 
-       if (old_state == LWSS_ESTABLISHED ||
+       if (wsi->state_pre_close == LWSS_ESTABLISHED ||
            wsi->mode == LWSCM_WS_SERVING ||
            wsi->mode == LWSCM_WS_CLIENT) {
 
-               lws_free_set_NULL(wsi->u.ws.rx_user_buffer);
+               if (wsi->u.ws.rx_draining_ext) {
+                       struct lws **w = &pt->rx_draining_ext_list;
+
+                       wsi->u.ws.rx_draining_ext = 0;
+                       /* remove us from context draining ext list */
+                       while (*w) {
+                               if (*w == wsi) {
+                                       *w = wsi->u.ws.rx_draining_ext_list;
+                                       break;
+                               }
+                               w = &((*w)->u.ws.rx_draining_ext_list);
+                       }
+                       wsi->u.ws.rx_draining_ext_list = NULL;
+               }
+
+               if (wsi->u.ws.tx_draining_ext) {
+                       struct lws **w = &pt->tx_draining_ext_list;
+
+                       wsi->u.ws.tx_draining_ext = 0;
+                       /* remove us from context draining ext list */
+                       while (*w) {
+                               if (*w == wsi) {
+                                       *w = wsi->u.ws.tx_draining_ext_list;
+                                       break;
+                               }
+                               w = &((*w)->u.ws.tx_draining_ext_list);
+                       }
+                       wsi->u.ws.tx_draining_ext_list = NULL;
+               }
+               lws_free_set_NULL(wsi->u.ws.rx_ubuf);
 
                if (wsi->trunc_alloc)
                        /* not going to be completed... nuke it */
@@ -259,10 +370,12 @@ just_kill_connection:
        /* tell the user it's all over for this guy */
 
        if (wsi->protocol && wsi->protocol->callback &&
-           ((old_state == LWSS_ESTABLISHED) ||
-           (old_state == LWSS_RETURNED_CLOSE_ALREADY) ||
-           (old_state == LWSS_AWAITING_CLOSE_ACK) ||
-           (old_state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE))) {
+           ((wsi->state_pre_close == LWSS_ESTABLISHED) ||
+           (wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) ||
+           (wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) ||
+           (wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) ||
+           (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
+           (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
                lwsl_debug("calling back CLOSED\n");
                wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
                                        wsi->user_space, NULL, 0);
@@ -278,30 +391,41 @@ just_kill_connection:
                                        wsi->user_space, NULL, 0);
        } else
                lwsl_debug("not calling back closed mode=%d state=%d\n",
-                          wsi->mode, old_state);
+                          wsi->mode, wsi->state_pre_close);
 
        /* deallocate any active extension contexts */
 
-       if (lws_ext_cb_wsi_active_exts(wsi, LWS_EXT_CALLBACK_DESTROY, NULL, 0) < 0)
+       if (lws_ext_cb_active(wsi, LWS_EXT_CB_DESTROY, NULL, 0) < 0)
                lwsl_warn("extension destruction failed\n");
-#ifndef LWS_NO_EXTENSIONS
-       for (n = 0; n < wsi->count_active_extensions; n++)
-               lws_free(wsi->active_extensions_user[n]);
-#endif
        /*
         * inform all extensions in case they tracked this guy out of band
         * even though not active on him specifically
         */
        if (lws_ext_cb_all_exts(context, wsi,
-                      LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
+                      LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
                lwsl_warn("ext destroy wsi failed\n");
 
+       wsi->socket_is_permanently_unusable = 1;
+
+#ifdef LWS_USE_LIBUV
+       if (LWS_LIBUV_ENABLED(context)) {
+               /* libuv has to do his own close handle processing asynchronously */
+               lws_libuv_closehandle(wsi);
+
+               return;
+       }
+#endif
+
+       lws_close_free_wsi_final(wsi);
+}
+
+void
+lws_close_free_wsi_final(struct lws *wsi)
+{
+       int n;
+
        if (!lws_ssl_close(wsi) && lws_socket_is_valid(wsi->sock)) {
 #if LWS_POSIX
-               n = shutdown(wsi->sock, SHUT_RDWR);
-               if (n)
-                       lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO);
-
                n = compatible_close(wsi->sock);
                if (n)
                        lwsl_debug("closing: close ret %d\n", LWS_ERRNO);
@@ -313,12 +437,26 @@ just_kill_connection:
        }
 
        /* outermost destroy notification for wsi (user_space still intact) */
-       context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
+       wsi->context->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY,
                                       wsi->user_space, NULL, 0);
 
        lws_free_wsi(wsi);
 }
 
+#if LWS_POSIX
+LWS_VISIBLE int
+interface_to_sa(struct lws_context *context, const char *ifname, struct sockaddr_in *addr, size_t addrlen)
+{
+       int ipv6 = 0;
+#ifdef LWS_USE_IPV6
+       ipv6 = LWS_IPV6_ENABLED(context);
+#endif
+       (void)context;
+
+       return lws_interface_to_sa(ipv6, ifname, addr, addrlen);
+}
+#endif
+
 LWS_VISIBLE int
 lws_get_addresses(struct lws_context *context, void *ads, char *name,
                  int name_len, char *rip, int rip_len)
@@ -386,7 +524,8 @@ lws_get_addresses(struct lws_context *context, void *ads, char *name,
        if (addr4.sin_family == AF_UNSPEC)
                return -1;
 
-       lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len);
+       if (lws_plat_inet_ntop(AF_INET, &addr4.sin_addr, rip, rip_len) == NULL)
+               return -1;
 
        return 0;
 #else
@@ -493,42 +632,25 @@ LWS_VISIBLE int
 lws_callback_all_protocol(struct lws_context *context,
                          const struct lws_protocols *protocol, int reason)
 {
+       struct lws_context_per_thread *pt = &context->pt[0];
+       unsigned int n, m = context->count_threads;
        struct lws *wsi;
-       int n;
 
-       for (n = 0; n < context->fds_count; n++) {
-               wsi = wsi_from_fd(context, context->fds[n].fd);
-               if (!wsi)
-                       continue;
-               if (wsi->protocol == protocol)
-                       protocol->callback(wsi, reason, wsi->user_space, NULL, 0);
+       while (m--) {
+               for (n = 0; n < pt->fds_count; n++) {
+                       wsi = wsi_from_fd(context, pt->fds[n].fd);
+                       if (!wsi)
+                               continue;
+                       if (wsi->protocol == protocol)
+                               protocol->callback(wsi, reason, wsi->user_space,
+                                                  NULL, 0);
+               }
+               pt++;
        }
 
        return 0;
 }
 
-/**
- * lws_set_timeout() - marks the wsi as subject to a timeout
- *
- * You will not need this unless you are doing something special
- *
- * @wsi:       Websocket connection instance
- * @reason:    timeout reason
- * @secs:      how many seconds
- */
-
-LWS_VISIBLE void
-lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs)
-{
-       time_t now;
-
-       time(&now);
-
-       wsi->pending_timeout_limit = now + secs;
-       wsi->pending_timeout = reason;
-}
-
-
 #if LWS_POSIX
 
 /**
@@ -627,15 +749,19 @@ LWS_VISIBLE void
 lws_rx_flow_allow_all_protocol(const struct lws_context *context,
                               const struct lws_protocols *protocol)
 {
-       int n;
+       const struct lws_context_per_thread *pt = &context->pt[0];
        struct lws *wsi;
-
-       for (n = 0; n < context->fds_count; n++) {
-               wsi = wsi_from_fd(context, context->fds[n].fd);
-               if (!wsi)
-                       continue;
-               if (wsi->protocol == protocol)
-                       lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
+       unsigned int n, m = context->count_threads;
+
+       while (m--) {
+               for (n = 0; n < pt->fds_count; n++) {
+                       wsi = wsi_from_fd(context, pt->fds[n].fd);
+                       if (!wsi)
+                               continue;
+                       if (wsi->protocol == protocol)
+                               lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW);
+               }
+               pt++;
        }
 }
 
@@ -655,7 +781,7 @@ lws_canonical_hostname(struct lws_context *context)
        return (const char *)context->canonical_hostname;
 }
 
-int user_callback_handle_rxflow(callback_function callback_function,
+int user_callback_handle_rxflow(lws_callback_function callback_function,
                                struct lws *wsi,
                                enum lws_callback_reasons reason, void *user,
                                void *in, size_t len)
@@ -763,7 +889,10 @@ lws_get_protocol(struct lws *wsi)
 LWS_VISIBLE int
 lws_is_final_fragment(struct lws *wsi)
 {
-       return wsi->u.ws.final && !wsi->u.ws.rx_packet_length;
+       lwsl_info("%s: final %d, rx pk length %d, draining %d", __func__,
+                       wsi->u.ws.final, wsi->u.ws.rx_packet_length,
+                       wsi->u.ws.rx_draining_ext);
+       return wsi->u.ws.final && !wsi->u.ws.rx_packet_length && !wsi->u.ws.rx_draining_ext;
 }
 
 LWS_VISIBLE unsigned char
@@ -796,18 +925,39 @@ lws_ensure_user_space(struct lws *wsi)
 
 LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
 {
+       time_t o_now = time(NULL);
        unsigned long long now;
+       struct tm *ptm = NULL;
        char buf[300];
+#ifndef WIN32
+       struct tm tm;
+#endif
        int n;
 
+#ifdef WIN32
+       ptm = localtime(&o_now);
+#else
+       if (localtime_r(&o_now, &tm))
+               ptm = &tm;
+#endif
        buf[0] = '\0';
        for (n = 0; n < LLL_COUNT; n++) {
                if (level != (1 << n))
                        continue;
                now = time_in_microseconds() / 100;
-               sprintf(buf, "[%llu:%04d] %s: ",
-                       (unsigned long long) now / 10000,
-                       (int)(now % 10000), log_level_names[n]);
+               if (ptm)
+                       sprintf(buf, "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %s: ",
+                               ptm->tm_year + 1900,
+                               ptm->tm_mon,
+                               ptm->tm_mday,
+                               ptm->tm_hour,
+                               ptm->tm_min,
+                               ptm->tm_sec,
+                               (int)(now % 10000), log_level_names[n]);
+               else
+                       sprintf(buf, "[%llu:%04d] %s: ",
+                                       (unsigned long long) now / 10000,
+                                       (int)(now % 10000), log_level_names[n]);
                break;
        }
 
@@ -947,6 +1097,12 @@ lws_get_context(const struct lws *wsi)
        return wsi->context;
 }
 
+LWS_VISIBLE LWS_EXTERN int
+lws_get_count_threads(struct lws_context *context)
+{
+       return context->count_threads;
+}
+
 LWS_VISIBLE LWS_EXTERN void *
 lws_wsi_user(struct lws *wsi)
 {
@@ -958,12 +1114,11 @@ lws_close_reason(struct lws *wsi, enum lws_close_status status,
                 unsigned char *buf, size_t len)
 {
        unsigned char *p, *start;
-       int budget = sizeof(wsi->u.ws.ping_payload_buf) -
-                    LWS_SEND_BUFFER_PRE_PADDING;
+       int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE;
 
        assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
 
-       start = p = &wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING];
+       start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE];
 
        *p++ = (((int)status) >> 8) & 0xff;
        *p++ = ((int)status) & 0xff;
@@ -979,8 +1134,10 @@ LWS_EXTERN int
 _lws_rx_flow_control(struct lws *wsi)
 {
        /* there is no pending change */
-       if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE))
+       if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) {
+               lwsl_debug("%s: no pending change\n", __func__);
                return 0;
+       }
 
        /* stuff is still buffered, not ready to really accept new input */
        if (wsi->rxflow_buffer) {
@@ -1009,3 +1166,142 @@ _lws_rx_flow_control(struct lws *wsi)
 
        return 0;
 }
+
+LWS_EXTERN int
+lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
+{
+       static const unsigned char e0f4[] = {
+               0xa0 | ((2 - 1) << 2) | 1, /* e0 */
+               0x80 | ((4 - 1) << 2) | 1, /* e1 */
+               0x80 | ((4 - 1) << 2) | 1, /* e2 */
+               0x80 | ((4 - 1) << 2) | 1, /* e3 */
+               0x80 | ((4 - 1) << 2) | 1, /* e4 */
+               0x80 | ((4 - 1) << 2) | 1, /* e5 */
+               0x80 | ((4 - 1) << 2) | 1, /* e6 */
+               0x80 | ((4 - 1) << 2) | 1, /* e7 */
+               0x80 | ((4 - 1) << 2) | 1, /* e8 */
+               0x80 | ((4 - 1) << 2) | 1, /* e9 */
+               0x80 | ((4 - 1) << 2) | 1, /* ea */
+               0x80 | ((4 - 1) << 2) | 1, /* eb */
+               0x80 | ((4 - 1) << 2) | 1, /* ec */
+               0x80 | ((2 - 1) << 2) | 1, /* ed */
+               0x80 | ((4 - 1) << 2) | 1, /* ee */
+               0x80 | ((4 - 1) << 2) | 1, /* ef */
+               0x90 | ((3 - 1) << 2) | 2, /* f0 */
+               0x80 | ((4 - 1) << 2) | 2, /* f1 */
+               0x80 | ((4 - 1) << 2) | 2, /* f2 */
+               0x80 | ((4 - 1) << 2) | 2, /* f3 */
+               0x80 | ((1 - 1) << 2) | 2, /* f4 */
+
+               0,                         /* s0 */
+               0x80 | ((4 - 1) << 2) | 0, /* s2 */
+               0x80 | ((4 - 1) << 2) | 1, /* s3 */
+       };
+       unsigned char s = *state;
+
+       while (len--) {
+               unsigned char c = *buf++;
+
+               if (!s) {
+                       if (c >= 0x80) {
+                               if (c < 0xc2 || c > 0xf4)
+                                       return 1;
+                               if (c < 0xe0)
+                                       s = 0x80 | ((4 - 1) << 2);
+                               else
+                                       s = e0f4[c - 0xe0];
+                       }
+               } else {
+                       if (c < (s & 0xf0) ||
+                           c >= (s & 0xf0) + 0x10 + ((s << 2) & 0x30))
+                               return 1;
+                       s = e0f4[21 + (s & 3)];
+               }
+       }
+
+       *state = s;
+
+       return 0;
+}
+
+/**
+ * lws_parse_uri:      cut up prot:/ads:port/path into pieces
+ *                     Notice it does so by dropping '\0' into input string
+ *                     and the leading / on the path is consequently lost
+ *
+ * @p:                 incoming uri string.. will get written to
+ * @prot:              result pointer for protocol part (https://)
+ * @ads:               result pointer for address part
+ * @port:              result pointer for port part
+ * @path:              result pointer for path part
+ */
+
+LWS_VISIBLE LWS_EXTERN int
+lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
+             const char **path)
+{
+       const char *end;
+       static const char *slash = "/";
+
+       /* cut up the location into address, port and path */
+       *prot = p;
+       while (*p && (*p != ':' || p[1] != '/' || p[2] != '/'))
+               p++;
+       if (!*p) {
+               end = p;
+               p = (char *)*prot;
+               *prot = end;
+       } else {
+               *p = '\0';
+               p += 3;
+       }
+       *ads = p;
+       if (!strcmp(*prot, "http") || !strcmp(*prot, "ws"))
+               *port = 80;
+       else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss"))
+               *port = 443;
+
+       while (*p && *p != ':' && *p != '/')
+               p++;
+       if (*p == ':') {
+               *p++ = '\0';
+               *port = atoi(p);
+               while (*p && *p != '/')
+                       p++;
+       }
+       *path = slash;
+       if (*p) {
+               *p++ = '\0';
+               if (*p)
+                       *path = p;
+       }
+
+       return 0;
+}
+
+#ifdef LWS_NO_EXTENSIONS
+
+/* we need to provide dummy callbacks for internal exts
+ * so user code runs when faced with a lib compiled with
+ * extensions disabled.
+ */
+
+int
+lws_extension_callback_pm_deflate(struct lws_context *context,
+                                  const struct lws_extension *ext,
+                                  struct lws *wsi,
+                                  enum lws_extension_callback_reasons reason,
+                                  void *user, void *in, size_t len)
+{
+       (void)context;
+       (void)ext;
+       (void)wsi;
+       (void)reason;
+       (void)user;
+       (void)in;
+       (void)len;
+
+       return 0;
+}
+#endif
+