defeat POLLOUT if socket in shutdown wait
[platform/upstream/libwebsockets.git] / lib / libwebsockets.c
index a4fb792..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
@@ -38,100 +38,158 @@ static const char * const log_level_names[] = {
 };
 
 void
-lws_free_wsi(struct libwebsocket *wsi)
+lws_free_wsi(struct lws *wsi)
 {
        if (!wsi)
                return;
 
        /* Protocol user data may be allocated either internally by lws
-        * or by specified the user. Important we don't free external user data */
-       if (wsi->protocol && wsi->protocol->per_session_data_size
-               && wsi->user_space && !wsi->user_space_externally_allocated) {
+        * or by specified the user.
+        * We should only free what we allocated. */
+       if (wsi->protocol && wsi->protocol->per_session_data_size &&
+           wsi->user_space && !wsi->user_space_externally_allocated)
                lws_free(wsi->user_space);
-       }
 
-       lws_free2(wsi->rxflow_buffer);
-       lws_free2(wsi->truncated_send_malloc);
+       lws_free_set_NULL(wsi->rxflow_buffer);
+       lws_free_set_NULL(wsi->trunc_alloc);
+
+       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);
+       }
 
-       // TODO: Probably should handle the union structs in wsi->u here depending
-       //       on connection mode as well. Too spaghetti for me to follow however...
+       wsi->context->count_wsi_allocated--;
+       lwsl_debug("%s: %p, remaining wsi %d\n", __func__, wsi,
+                       wsi->context->count_wsi_allocated);
 
-       lws_free_header_table(wsi);
        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_and_free_session(struct libwebsocket_context *context,
-                        struct libwebsocket *wsi, enum lws_close_status reason)
+lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 {
+       struct lws_context *context;
+       struct lws_context_per_thread *pt;
        int n, m, ret;
-       int old_state;
-       unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 +
-                                                 LWS_SEND_BUFFER_POST_PADDING];
        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) {
+               lwsl_debug("closing http file\n");
+               lws_plat_file_close(wsi, wsi->u.http.fd);
+               wsi->u.http.fd = LWS_INVALID_FILE;
+               context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+                                              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) {
-       case WSI_STATE_DEAD_SOCKET:
+       wsi->state_pre_close = wsi->state;
+
+       switch (wsi->state_pre_close) {
+       case LWSS_DEAD_SOCKET:
                return;
 
        /* we tried the polite way... */
-       case WSI_STATE_AWAITING_CLOSE_ACK:
+       case LWSS_AWAITING_CLOSE_ACK:
                goto just_kill_connection;
 
-       case WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE:
-               if (wsi->truncated_send_len) {
-                       lws_callback_on_writable(context, wsi);
+       case LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE:
+               if (wsi->trunc_len) {
+                       lws_callback_on_writable(wsi);
                        return;
                }
-               lwsl_info("wsi %p completed WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
+               lwsl_info("wsi %p completed LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
                goto just_kill_connection;
        default:
-               if (wsi->truncated_send_len) {
-                       lwsl_info("wsi %p entering WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
-                       wsi->state = WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE;
+               if (wsi->trunc_len) {
+                       lwsl_info("wsi %p entering LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
+                       wsi->state = LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE;
                        lws_set_timeout(wsi, PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
                        return;
                }
                break;
        }
 
-       wsi->u.ws.close_reason = reason;
-
-       if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT ||
-                       wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE)
+       if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
+           wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
                goto just_kill_connection;
 
-       if (wsi->mode == LWS_CONNMODE_HTTP_SERVING)
-               context->protocols[0].callback(context, wsi,
-                       LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
-
-       if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
-               if (wsi->u.http.fd != LWS_INVALID_FILE) {
-                       // TODO: If we're just closing with LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY this file descriptor might leak?
-                       lwsl_debug("closing http file\n");
-                       compatible_file_close(wsi->u.http.fd);
-                       wsi->u.http.fd = LWS_INVALID_FILE;
-                       context->protocols[0].callback(context, wsi,
-                               LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
-               }
-       }
+       if (wsi->mode == LWSCM_HTTP_SERVING)
+               context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+                                              wsi->user_space, NULL, 0);
 
        /*
         * are his extensions okay with him closing?  Eg he might be a mux
         * parent and just his ch1 aspect is closing?
         */
-       
-       if (lws_ext_callback_for_each_active(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_and_free_session(struct libwebsocket_context *context,
 
                /* show every extension the new incoming data */
 
-               m = lws_ext_callback_for_each_active(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;
@@ -165,7 +223,8 @@ lws_close_and_free_session(struct libwebsocket_context *context,
 
                if (eff_buf.token_len)
                        if (lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
-                                     eff_buf.token_len) != eff_buf.token_len) {
+                                         eff_buf.token_len) !=
+                           eff_buf.token_len) {
                                lwsl_debug("close: ext spill failed\n");
                                goto just_kill_connection;
                        }
@@ -176,40 +235,43 @@ lws_close_and_free_session(struct libwebsocket_context *context,
         * add any necessary version-specific stuff.  If the write fails,
         * no worries we are closing anyway.  If we didn't initiate this
         * close, then our state has been changed to
-        * WSI_STATE_RETURNED_CLOSE_ALREADY and we will skip this.
+        * LWSS_RETURNED_CLOSE_ALREADY and we will skip this.
         *
         * Likewise if it's a second call to close this connection after we
         * sent the close indication to the peer already, we are in state
-        * WSI_STATE_AWAITING_CLOSE_ACK and will skip doing this a second time.
+        * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
         */
 
-       if (old_state == WSI_STATE_ESTABLISHED &&
-           reason != LWS_CLOSE_STATUS_NOSTATUS &&
-           reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY) {
-
+       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)))) {
                lwsl_debug("sending close indication...\n");
 
-               /* make valgrind happy */
-               memset(buf, 0, sizeof(buf));
-               n = lws_write(wsi,
-                               &buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
-                                                           0, LWS_WRITE_CLOSE);
+               /* 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_PRE] =
+                               (reason >> 16) & 0xff;
+                       wsi->u.ws.ping_payload_buf[LWS_PRE + 1] =
+                               reason & 0xff;
+               }
+
+               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) {
                        /*
                         * we have sent a nice protocol level indication we
                         * now wish to close, we should not send anything more
                         */
-
-                       wsi->state = WSI_STATE_AWAITING_CLOSE_ACK;
+                       wsi->state = LWSS_AWAITING_CLOSE_ACK;
 
                        /*
                         * ...and we should wait for a reply for a bit
                         * out of politeness
                         */
-
-                       lws_set_timeout(wsi,
-                                                 PENDING_TIMEOUT_CLOSE_ACK, 1);
-
+                       lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
                        lwsl_debug("sent close indication, awaiting ack\n");
 
                        return;
@@ -222,90 +284,148 @@ lws_close_and_free_session(struct libwebsocket_context *context,
 
 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);
 
-       lws_ssl_remove_wsi_from_buffered_list(context, wsi);
+       /* checking return redundant since we anyway close */
+       remove_wsi_socket_from_fds(wsi);
 
-       // checking return redundant since we anyway close
-       remove_wsi_socket_from_fds(context, wsi);
+       wsi->state = LWSS_DEAD_SOCKET;
 
-       wsi->state = WSI_STATE_DEAD_SOCKET;
+       lws_free_set_NULL(wsi->rxflow_buffer);
 
-       lws_free2(wsi->rxflow_buffer);
-       lws_free_header_table(wsi);
+       if (wsi->state_pre_close == LWSS_ESTABLISHED ||
+           wsi->mode == LWSCM_WS_SERVING ||
+           wsi->mode == LWSCM_WS_CLIENT) {
 
-       if ((old_state == WSI_STATE_ESTABLISHED ||
-            wsi->mode == LWS_CONNMODE_WS_SERVING ||
-            wsi->mode == LWS_CONNMODE_WS_CLIENT)) {
+               if (wsi->u.ws.rx_draining_ext) {
+                       struct lws **w = &pt->rx_draining_ext_list;
 
-               lws_free2(wsi->u.ws.rx_user_buffer);
-
-               if (wsi->truncated_send_malloc) {
-                       /* not going to be completed... nuke it */
-                       lws_free2(wsi->truncated_send_malloc);
-                       wsi->truncated_send_len = 0;
+                       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.ping_payload_buf) {
-                       lws_free2(wsi->u.ws.ping_payload_buf);
-                       wsi->u.ws.ping_payload_alloc = 0;
-                       wsi->u.ws.ping_payload_len = 0;
-                       wsi->u.ws.ping_pending_flag = 0;
+
+               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 */
+                       lws_free_set_NULL(wsi->trunc_alloc);
+
+               wsi->u.ws.ping_payload_len = 0;
+               wsi->u.ws.ping_pending_flag = 0;
        }
 
        /* tell the user it's all over for this guy */
 
        if (wsi->protocol && wsi->protocol->callback &&
-                       ((old_state == WSI_STATE_ESTABLISHED) ||
-                        (old_state == WSI_STATE_RETURNED_CLOSE_ALREADY) ||
-                        (old_state == WSI_STATE_AWAITING_CLOSE_ACK) ||
-                        (old_state == WSI_STATE_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(context, wsi, LWS_CALLBACK_CLOSED,
-                                                     wsi->user_space, NULL, 0);
-       } else if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) {
+               wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
+                                       wsi->user_space, NULL, 0);
+       } else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
                lwsl_debug("calling back CLOSED_HTTP\n");
-               context->protocols[0].callback(context, wsi,
-                       LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 );
-       } else if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY ||
-                  wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT) {
+               context->protocols[0].callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
+                                              wsi->user_space, NULL, 0 );
+       } else if (wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
+                  wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
                lwsl_debug("Connection closed before server reply\n");
-               context->protocols[0].callback(context, wsi,
-                               LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
-                               wsi->user_space, NULL, 0 );
+               context->protocols[0].callback(wsi,
+                                       LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+                                       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_callback_for_each_active(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_callback_for_each_extension_type(context, wsi,
-                      LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
+       if (lws_ext_cb_all_exts(context, wsi,
+                      LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0)
                lwsl_warn("ext destroy wsi failed\n");
 
-/*     lwsl_info("closing fd=%d\n", wsi->sock); */
+       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);
@@ -317,22 +437,36 @@ just_kill_connection:
        }
 
        /* outermost destroy notification for wsi (user_space still intact) */
-       context->protocols[0].callback(context, wsi,
-                       LWS_CALLBACK_WSI_DESTROY, wsi->user_space, NULL, 0);
+       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
-lws_get_addresses(struct libwebsocket_context *context,
-                           void *ads, char *name, int name_len,
-                           char *rip, int rip_len)
+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)
 {
 #if LWS_POSIX
        struct addrinfo ai, *res;
        struct sockaddr_in addr4;
 
-       rip[0] = '\0';
+       if (rip)
+               rip[0] = '\0';
        name[0] = '\0';
        addr4.sin_family = AF_UNSPEC;
 
@@ -350,7 +484,7 @@ lws_get_addresses(struct libwebsocket_context *context,
                getnameinfo((struct sockaddr *)ads,
                                sizeof(struct sockaddr_in6), name,
                                                        name_len, NULL, 0, 0);
-               
+
                return 0;
        } else
 #endif
@@ -390,7 +524,8 @@ lws_get_addresses(struct libwebsocket_context *context,
        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
@@ -407,8 +542,7 @@ lws_get_addresses(struct libwebsocket_context *context,
 
 /**
  * lws_get_peer_addresses() - Get client address information
- * @context:   Libwebsockets context
- * @wsi:       Local struct libwebsocket associated with
+ * @wsi:       Local struct lws associated with
  * @fd:                Connection socket descriptor
  * @name:      Buffer to take client address name
  * @name_len:  Length of client address name buffer
@@ -422,9 +556,8 @@ lws_get_addresses(struct libwebsocket_context *context,
  */
 
 LWS_VISIBLE void
-lws_get_peer_addresses(struct libwebsocket_context *context,
-       struct libwebsocket *wsi, lws_sockfd_type fd, char *name, int name_len,
-                                       char *rip, int rip_len)
+lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name,
+                      int name_len, char *rip, int rip_len)
 {
 #if LWS_POSIX
        socklen_t len;
@@ -432,6 +565,7 @@ lws_get_peer_addresses(struct libwebsocket_context *context,
        struct sockaddr_in6 sin6;
 #endif
        struct sockaddr_in sin4;
+       struct lws_context *context = wsi->context;
        int ret = -1;
        void *p;
 
@@ -455,13 +589,12 @@ lws_get_peer_addresses(struct libwebsocket_context *context,
                lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO));
                goto bail;
        }
-       
+
        ret = lws_get_addresses(context, p, name, name_len, rip, rip_len);
 
 bail:
        lws_latency(context, wsi, "lws_get_peer_addresses", ret, 1);
 #else
-       (void)context;
        (void)wsi;
        (void)fd;
        (void)name;
@@ -481,7 +614,7 @@ bail:
  *     using globals statics in the user code.
  */
 LWS_EXTERN void *
-lws_context_user(struct libwebsocket_context *context)
+lws_context_user(struct lws_context *context)
 {
        return context->user_space;
 }
@@ -496,48 +629,28 @@ lws_context_user(struct libwebsocket_context *context)
  */
 
 LWS_VISIBLE int
-lws_callback_all_protocol(
-               const struct libwebsocket_protocols *protocol, int reason)
+lws_callback_all_protocol(struct lws_context *context,
+                         const struct lws_protocols *protocol, int reason)
 {
-       struct libwebsocket_context *context = protocol->owning_server;
-       int n;
-       struct libwebsocket *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)
-                       protocol->callback(context, wsi,
-                                       reason, wsi->user_space, NULL, 0);
+       struct lws_context_per_thread *pt = &context->pt[0];
+       unsigned int n, m = context->count_threads;
+       struct lws *wsi;
+
+       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 libwebsocket *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
 
 /**
@@ -549,7 +662,7 @@ lws_set_timeout(struct libwebsocket *wsi,
  */
 
 LWS_VISIBLE int
-lws_get_socket_fd(struct libwebsocket *wsi)
+lws_get_socket_fd(struct lws *wsi)
 {
        return wsi->sock;
 }
@@ -558,8 +671,8 @@ lws_get_socket_fd(struct libwebsocket *wsi)
 
 #ifdef LWS_LATENCY
 void
-lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi,
-                                    const char *action, int ret, int completed)
+lws_latency(struct lws_context *context, struct lws *wsi, const char *action,
+           int ret, int completed)
 {
        unsigned long long u;
        char buf[256];
@@ -611,12 +724,12 @@ lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi,
  */
 
 LWS_VISIBLE int
-lws_rx_flow_control(struct libwebsocket *wsi, int enable)
+lws_rx_flow_control(struct lws *wsi, int enable)
 {
        if (enable == (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW))
                return 0;
 
-       lwsl_info("lws_rx_flow_control(0x%p, %d)\n", wsi, enable);
+       lwsl_info("%s: (0x%p, %d)\n", __func__, wsi, enable);
        wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !!enable;
 
        return 0;
@@ -633,19 +746,22 @@ lws_rx_flow_control(struct libwebsocket *wsi, int enable)
  */
 
 LWS_VISIBLE void
-lws_rx_flow_allow_all_protocol(
-                               const struct libwebsocket_protocols *protocol)
+lws_rx_flow_allow_all_protocol(const struct lws_context *context,
+                              const struct lws_protocols *protocol)
 {
-       struct libwebsocket_context *context = protocol->owning_server;
-       int n;
-       struct libwebsocket *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);
+       const struct lws_context_per_thread *pt = &context->pt[0];
+       struct lws *wsi;
+       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++;
        }
 }
 
@@ -660,20 +776,19 @@ lws_rx_flow_allow_all_protocol(
  * @context:   Websocket context
  */
 LWS_VISIBLE extern const char *
-lws_canonical_hostname(struct libwebsocket_context *context)
+lws_canonical_hostname(struct lws_context *context)
 {
        return (const char *)context->canonical_hostname;
 }
 
-int user_callback_handle_rxflow(callback_function callback_function,
-               struct libwebsocket_context *context,
-                       struct libwebsocket *wsi,
-                        enum libwebsocket_callback_reasons reason, void *user,
-                                                         void *in, size_t len)
+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)
 {
        int n;
 
-       n = callback_function(context, wsi, reason, user, in, len);
+       n = callback_function(wsi, reason, user, in, len);
        if (!n)
                n = _lws_rx_flow_control(wsi);
 
@@ -682,29 +797,29 @@ int user_callback_handle_rxflow(callback_function callback_function,
 
 
 /**
- * lws_set_proxy() - Setups proxy to libwebsocket_context.
- * @context:   pointer to struct libwebsocket_context you want set proxy to
+ * lws_set_proxy() - Setups proxy to lws_context.
+ * @context:   pointer to struct lws_context you want set proxy to
  * @proxy: pointer to c string containing proxy in format address:port
  *
- * Returns 0 if proxy string was parsed and proxy was setup. 
+ * Returns 0 if proxy string was parsed and proxy was setup.
  * Returns -1 if @proxy is NULL or has incorrect format.
  *
  * This is only required if your OS does not provide the http_proxy
  * environment variable (eg, OSX)
  *
  *   IMPORTANT! You should call this function right after creation of the
- *   libwebsocket_context and before call to connect. If you call this
+ *   lws_context and before call to connect. If you call this
  *   function after connect behavior is undefined.
- *   This function will override proxy settings made on libwebsocket_context
+ *   This function will override proxy settings made on lws_context
  *   creation with genenv() call.
  */
 
 LWS_VISIBLE int
-lws_set_proxy(struct libwebsocket_context *context, const char *proxy)
+lws_set_proxy(struct lws_context *context, const char *proxy)
 {
        char *p;
        char authstring[96];
-       
+
        if (!proxy)
                return -1;
 
@@ -722,7 +837,7 @@ lws_set_proxy(struct libwebsocket_context *context, const char *proxy)
                        goto auth_too_long;
 
                lwsl_notice(" Proxy auth in use\n");
-                       
+
                proxy = p + 1;
        } else
                context->proxy_basic_auth_token[0] = '\0';
@@ -738,8 +853,10 @@ lws_set_proxy(struct libwebsocket_context *context, const char *proxy)
 
                return -1;
        } else {
-               *p = '\0';
-               context->http_proxy_port = atoi(p + 1);
+               if (p) {
+                       *p = '\0';
+                       context->http_proxy_port = atoi(p + 1);
+               }
        }
 
        lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address,
@@ -763,26 +880,29 @@ auth_too_long:
  *     this is how you can get a pointer to the active protocol if needed.
  */
 
-LWS_VISIBLE const struct libwebsocket_protocols *
-lws_get_protocol(struct libwebsocket *wsi)
+LWS_VISIBLE const struct lws_protocols *
+lws_get_protocol(struct lws *wsi)
 {
        return wsi->protocol;
 }
 
 LWS_VISIBLE int
-lws_is_final_fragment(struct libwebsocket *wsi)
+lws_is_final_fragment(struct lws *wsi)
 {
-       return wsi->u.ws.final;
+       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
-lws_get_reserved_bits(struct libwebsocket *wsi)
+lws_get_reserved_bits(struct lws *wsi)
 {
        return wsi->u.ws.rsv;
 }
 
 int
-lws_ensure_user_space(struct libwebsocket *wsi)
+lws_ensure_user_space(struct lws *wsi)
 {
        lwsl_info("%s: %p protocol %p\n", __func__, wsi, wsi->protocol);
        if (!wsi->protocol)
@@ -797,29 +917,53 @@ lws_ensure_user_space(struct libwebsocket *wsi)
                        return 1;
                }
        } else
-               lwsl_info("%s: %p protocol pss %u, user_space=%d\n", __func__, wsi, wsi->protocol->per_session_data_size, wsi->user_space);
+               lwsl_info("%s: %p protocol pss %u, user_space=%d\n",
+                         __func__, wsi, wsi->protocol->per_session_data_size,
+                         wsi->user_space);
        return 0;
 }
 
 LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line)
 {
-       char buf[300];
+       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)) {
-                       now = time_in_microseconds() / 100;
-                       sprintf(buf, "[%llu:%04d] %s: ", (unsigned long long) now / 10000,
+       for (n = 0; n < LLL_COUNT; n++) {
+               if (level != (1 << n))
+                       continue;
+               now = time_in_microseconds() / 100;
+               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]);
-                       break;
-               }
+               else
+                       sprintf(buf, "[%llu:%04d] %s: ",
+                                       (unsigned long long) now / 10000,
+                                       (int)(now % 10000), log_level_names[n]);
+               break;
+       }
 
        fprintf(stderr, "%s%s", buf, line);
 }
 
-
 LWS_VISIBLE void _lws_logv(int filter, const char *format, va_list vl)
 {
        char buf[256];
@@ -853,12 +997,12 @@ LWS_VISIBLE void _lws_log(int filter, const char *format, ...)
  *     emission on stderr.
  */
 
-LWS_VISIBLE void lws_set_log_level(int level, void (*log_emit_function)(int level,
-                                                             const char *line))
+LWS_VISIBLE void lws_set_log_level(int level,
+                                  void (*func)(int level, const char *line))
 {
        log_level = level;
-       if (log_emit_function)
-               lwsl_emit = log_emit_function;
+       if (func)
+               lwsl_emit = func;
 }
 
 /**
@@ -870,7 +1014,7 @@ LWS_VISIBLE void lws_set_log_level(int level, void (*log_emit_function)(int leve
  *     checked (appears for client wsi told to skip check on connection)
  */
 LWS_VISIBLE int
-lws_is_ssl(struct libwebsocket *wsi)
+lws_is_ssl(struct lws *wsi)
 {
 #ifdef LWS_OPENSSL_SUPPORT
        return wsi->use_ssl;
@@ -888,45 +1032,44 @@ lws_is_ssl(struct libwebsocket *wsi)
  * write on this connection is still buffered, and can't be cleared without
  * returning to the service loop and waiting for the connection to be
  * writeable again.
- * 
+ *
  * If you will try to do >1 lws_write call inside a single
  * WRITEABLE callback, you must check this after every write and bail if
  * set, ask for a new writeable callback and continue writing from there.
- * 
+ *
  * This is never set at the start of a writeable callback, but any write
  * may set it.
  */
 
 LWS_VISIBLE int
-lws_partial_buffered(struct libwebsocket *wsi)
+lws_partial_buffered(struct lws *wsi)
 {
-       return !!wsi->truncated_send_len;       
+       return !!wsi->trunc_len;
 }
 
-void lws_set_protocol_write_pending(struct libwebsocket_context *context,
-                                   struct libwebsocket *wsi,
+void lws_set_protocol_write_pending(struct lws *wsi,
                                    enum lws_pending_protocol_send pend)
 {
        lwsl_info("setting pps %d\n", pend);
-       
+
        if (wsi->pps)
                lwsl_err("pps overwrite\n");
        wsi->pps = pend;
        lws_rx_flow_control(wsi, 0);
-       lws_callback_on_writable(context, wsi);
+       lws_callback_on_writable(wsi);
 }
 
 LWS_VISIBLE size_t
-lws_get_peer_write_allowance(struct libwebsocket *wsi)
+lws_get_peer_write_allowance(struct lws *wsi)
 {
 #ifdef LWS_USE_HTTP2
        /* only if we are using HTTP2 on this connection */
-       if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING)
+       if (wsi->mode != LWSCM_HTTP2_SERVING)
                return -1;
        /* user is only interested in how much he can send, or that he can't  */
        if (wsi->u.http2.tx_credit <= 0)
                return 0;
-       
+
        return wsi->u.http2.tx_credit;
 #else
        (void)wsi;
@@ -935,8 +1078,230 @@ lws_get_peer_write_allowance(struct libwebsocket *wsi)
 }
 
 LWS_VISIBLE void
-lws_union_transition(struct libwebsocket *wsi, enum connection_mode mode)
+lws_union_transition(struct lws *wsi, enum connection_mode mode)
 {
+       lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode);
        memset(&wsi->u, 0, sizeof(wsi->u));
        wsi->mode = mode;
 }
+
+LWS_VISIBLE struct lws_plat_file_ops *
+lws_get_fops(struct lws_context *context)
+{
+       return &context->fops;
+}
+
+LWS_VISIBLE LWS_EXTERN struct lws_context *
+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)
+{
+       return wsi->user_space;
+}
+
+LWS_VISIBLE LWS_EXTERN void
+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_PRE;
+
+       assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
+
+       start = p = &wsi->u.ws.ping_payload_buf[LWS_PRE];
+
+       *p++ = (((int)status) >> 8) & 0xff;
+       *p++ = ((int)status) & 0xff;
+
+       if (buf)
+               while (len-- && p < start + budget)
+                       *p++ = *buf++;
+
+       wsi->u.ws.close_in_ping_buffer_len = p - start;
+}
+
+LWS_EXTERN int
+_lws_rx_flow_control(struct lws *wsi)
+{
+       /* there is no 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) {
+               /* get ourselves called back to deal with stashed buffer */
+               lws_callback_on_writable(wsi);
+               return 0;
+       }
+
+       /* pending is cleared, we can change rxflow state */
+
+       wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE;
+
+       lwsl_info("rxflow: wsi %p change_to %d\n", wsi,
+                             wsi->rxflow_change_to & LWS_RXFLOW_ALLOW);
+
+       /* adjust the pollfd for this wsi */
+
+       if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) {
+               if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) {
+                       lwsl_info("%s: fail\n", __func__);
+                       return -1;
+               }
+       } else
+               if (lws_change_pollfd(wsi, LWS_POLLIN, 0))
+                       return -1;
+
+       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
+