defeat POLLOUT if socket in shutdown wait
[platform/upstream/libwebsockets.git] / lib / libwebsockets.c
index d1d9580..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;
 
@@ -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)))) {
@@ -199,8 +257,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
                                reason & 0xff;
                }
 
-               n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[
-                                               LWS_PRE],
+               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: %p\n", wsi);
+#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,12 +324,12 @@ 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) {
 
                if (wsi->u.ws.rx_draining_ext) {
-                       struct lws **w = &wsi->context->rx_draining_ext_list;
+                       struct lws **w = &pt->rx_draining_ext_list;
 
                        wsi->u.ws.rx_draining_ext = 0;
                        /* remove us from context draining ext list */
@@ -262,7 +344,7 @@ just_kill_connection:
                }
 
                if (wsi->u.ws.tx_draining_ext) {
-                       struct lws **w = &wsi->context->tx_draining_ext_list;
+                       struct lws **w = &pt->tx_draining_ext_list;
 
                        wsi->u.ws.tx_draining_ext = 0;
                        /* remove us from context draining ext list */
@@ -288,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);
@@ -307,7 +391,7 @@ 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 */
 
@@ -321,12 +405,27 @@ just_kill_connection:
                       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);
@@ -338,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)
@@ -411,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
@@ -518,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
 
 /**
@@ -652,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++;
        }
 }
 
@@ -789,7 +890,8 @@ LWS_VISIBLE int
 lws_is_final_fragment(struct lws *wsi)
 {
        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);
+                       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;
 }
 
@@ -823,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;
        }
 
@@ -974,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)
 {
@@ -985,8 +1114,7 @@ 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;
+       int budget = sizeof(wsi->u.ws.ping_payload_buf) - LWS_PRE;
 
        assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
 
@@ -1007,7 +1135,7 @@ _lws_rx_flow_control(struct lws *wsi)
 {
        /* there is no pending change */
        if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) {
-               lwsl_info("%s: no pending change\n", __func__);
+               lwsl_debug("%s: no pending change\n", __func__);
                return 0;
        }
 
@@ -1097,8 +1225,9 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
 }
 
 /**
- * lws_parse_uri:      cut up https:/xxx:yyy/zzz into pieces
+ * 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://)
@@ -1108,7 +1237,8 @@ lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len)
  */
 
 LWS_VISIBLE LWS_EXTERN int
-lws_parse_uri(char *p, const char **prot, const char **ads, int *port, const char **path)
+lws_parse_uri(char *p, const char **prot, const char **ads, int *port,
+             const char **path)
 {
        const char *end;
        static const char *slash = "/";
@@ -1130,7 +1260,7 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port, const cha
                *port = 80;
        else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss"))
                *port = 443;
-       
+
        while (*p && *p != ':' && *p != '/')
                p++;
        if (*p == ':') {
@@ -1163,6 +1293,14 @@ lws_extension_callback_pm_deflate(struct lws_context *context,
                                   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