ssl pending buffered reads use linked list
authorAndy Green <andy.green@linaro.org>
Thu, 29 Jan 2015 00:36:18 +0000 (08:36 +0800)
committerAndy Green <andy.green@linaro.org>
Thu, 29 Jan 2015 00:36:18 +0000 (08:36 +0800)
Signed-off-by: Andy Green <andy.green@linaro.org>
lib/libwebsockets.c
lib/lws-plat-unix.c
lib/private-libwebsockets.h
lib/ssl.c

index 2295cc0..1d8af8d 100644 (file)
@@ -209,6 +209,8 @@ just_kill_connection:
         * delete socket from the internal poll list if still present
         */
 
+       lws_ssl_remove_wsi_from_buffered_list(context, wsi);
+
        remove_wsi_socket_from_fds(context, wsi);
 
        wsi->state = WSI_STATE_DEAD_SOCKET;
index 1b83187..a894e80 100644 (file)
@@ -97,6 +97,7 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
        int n;
        int m;
        char buf;
+       struct libwebsocket *wsi, *wsi_next;
 
        /* stay dead once we are dead */
 
@@ -110,25 +111,20 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
 
 #ifdef LWS_OPENSSL_SUPPORT
        /* if we know we have non-network pending data, do not wait in poll */
-       if (context->ssl_flag_buffered_reads)
+       if (lws_ssl_anybody_has_buffered_read(context))
                timeout_ms = 0;
 #endif
        n = poll(context->fds, context->fds_count, timeout_ms);
        context->service_tid = 0;
 
 #ifdef LWS_OPENSSL_SUPPORT
-       if (!context->ssl_flag_buffered_reads && n == 0) {
+       if (!lws_ssl_anybody_has_buffered_read(context) && n == 0) {
 #else
        if (n == 0) /* poll timeout */ {
 #endif
                libwebsocket_service_fd(context, NULL);
                return 0;
        }
-       
-#ifdef LWS_OPENSSL_SUPPORT
-       /* any more will have to set it fresh this time around */
-       context->ssl_flag_buffered_reads = 0;
-#endif
 
        if (n < 0) {
                if (LWS_ERRNO != LWS_EINTR)
@@ -136,29 +132,36 @@ lws_plat_service(struct libwebsocket_context *context, int timeout_ms)
                return 0;
        }
 
-       /* any socket with events to service? */
-
-       for (n = 0; n < context->fds_count; n++) {
 #ifdef LWS_OPENSSL_SUPPORT
-               struct libwebsocket *wsi;
-               
-               wsi = context->lws_lookup[context->fds[n].fd];
-               if (wsi == NULL)
-                       continue;
-               /* 
-                * if he's not flowcontrolled, make sure we service ssl
-                * pending read data
-                */
-               if (wsi->ssl && wsi->buffered_reads_pending) {
-                       lwsl_debug("wsi %p: forcing POLLIN\n", wsi);
-                       context->fds[n].revents |= context->fds[n].events & POLLIN;
-                       if (context->fds[n].revents & POLLIN)
-                               wsi->buffered_reads_pending = 0;
-                       else
-                               /* somebody left with pending SSL read data */
-                               context->ssl_flag_buffered_reads = 1;
+       /*
+        * For all guys with buffered SSL read data already saved up, if they
+        * are not flowcontrolled, fake their POLLIN status so they'll get
+        * service to use up the buffered incoming data, even though their
+        * network socket may have nothing
+        */
+
+       wsi = context->pending_read_list;
+       while (wsi) {
+               wsi_next = wsi->pending_read_list_next;
+               context->fds[wsi->sock].revents |=
+                               context->fds[wsi->sock].events & POLLIN;
+               if (context->fds[wsi->sock].revents & POLLIN) {
+                       /*
+                        * he's going to get serviced now, take him off the
+                        * list of guys with buffered SSL.  If he still has some
+                        * at the end of the service, he'll get put back on the
+                        * list then.
+                        */
+                       lws_ssl_remove_wsi_from_buffered_list(context, wsi);
                }
+               wsi = wsi_next;
+       }
 #endif
+
+       /* any socket with events to service? */
+
+       for (n = 0; n < context->fds_count; n++) {
+
                if (!context->fds[n].revents)
                        continue;
 
index 9c76070..34667b4 100755 (executable)
@@ -474,7 +474,10 @@ struct libwebsocket_context {
        unsigned int user_supplied_ssl_ctx:1;
        SSL_CTX *ssl_ctx;
        SSL_CTX *ssl_client_ctx;
-       unsigned int ssl_flag_buffered_reads:1;
+       struct libwebsocket *pending_read_list; /* linked list */
+#define lws_ssl_anybody_has_buffered_read(ctx) (ctx->use_ssl && ctx->pending_read_list)
+#else
+#define lws_ssl_anybody_has_buffered_read(ctx) (0)
 #endif
        struct libwebsocket_protocols *protocols;
        int count_protocols;
@@ -840,8 +843,8 @@ struct libwebsocket {
 #ifdef LWS_OPENSSL_SUPPORT
        SSL *ssl;
        BIO *client_bio;
+       struct libwebsocket *pending_read_list_prev, *pending_read_list_next;
        unsigned int use_ssl:2;
-       unsigned int buffered_reads_pending:1;
        unsigned int upgraded:1;
 #endif
 
@@ -1089,6 +1092,7 @@ enum lws_ssl_capable_status {
 #define lws_server_socket_service_ssl(_a, _b, _c, _d, _e) (0)
 #define lws_ssl_close(_a) (0)
 #define lws_ssl_context_destroy(_a)
+#define lws_ssl_remove_wsi_from_buffered_list(_a, _b)
 #else
 #define LWS_SSL_ENABLED(context) (context->use_ssl)
 LWS_EXTERN int openssl_websocket_private_data_index;
@@ -1106,6 +1110,9 @@ LWS_EXTERN int
 lws_ssl_close(struct libwebsocket *wsi);
 LWS_EXTERN void
 lws_ssl_context_destroy(struct libwebsocket_context *context);
+LWS_VISIBLE void
+lws_ssl_remove_wsi_from_buffered_list(struct libwebsocket_context *context,
+                    struct libwebsocket *wsi);
 #ifndef LWS_NO_SERVER
 LWS_EXTERN int
 lws_context_init_server_ssl(struct lws_context_creation_info *info,
index a6be4fb..19d7333 100644 (file)
--- a/lib/ssl.c
+++ b/lib/ssl.c
@@ -392,6 +392,32 @@ int lws_context_init_client_ssl(struct lws_context_creation_info *info,
 }
 #endif
 
+LWS_VISIBLE void
+lws_ssl_remove_wsi_from_buffered_list(struct libwebsocket_context *context,
+                    struct libwebsocket *wsi)
+{
+       if (!wsi->pending_read_list_prev &&
+           !wsi->pending_read_list_next &&
+           context->pending_read_list != wsi)
+               /* we are not on the list */
+               return;
+
+       /* point previous guy's next to our next */
+       if (!wsi->pending_read_list_prev)
+               context->pending_read_list = wsi->pending_read_list_next;
+       else
+               wsi->pending_read_list_prev->pending_read_list_next =
+                       wsi->pending_read_list_next;
+
+       /* point next guy's previous to our previous */
+       if (wsi->pending_read_list_next)
+               wsi->pending_read_list_next->pending_read_list_prev =
+                       wsi->pending_read_list_prev;
+
+       wsi->pending_read_list_prev = NULL;
+       wsi->pending_read_list_next = NULL;
+}
+
 LWS_VISIBLE int
 lws_ssl_capable_read(struct libwebsocket_context *context,
                     struct libwebsocket *wsi, unsigned char *buf, int len)
@@ -400,8 +426,6 @@ lws_ssl_capable_read(struct libwebsocket_context *context,
 
        if (!wsi->ssl)
                return lws_ssl_capable_read_no_ssl(context, wsi, buf, len);
-       
-       wsi->buffered_reads_pending = 0;
 
        n = SSL_read(wsi->ssl, buf, len);
        if (n >= 0) {
@@ -413,10 +437,14 @@ lws_ssl_capable_read(struct libwebsocket_context *context,
                 * and if we don't realize, this data will sit there forever
                 */
                if (n == len && wsi->ssl && SSL_pending(wsi->ssl)) {
-                       context->ssl_flag_buffered_reads = 1;
-                       wsi->buffered_reads_pending = 1;
+                       assert(!wsi->pending_read_list_next && !wsi->pending_read_list_prev);
+                       /* add us to the linked list of guys with pending ssl */
+                       context->pending_read_list->pending_read_list_prev = wsi;
+                       wsi->pending_read_list_next = context->pending_read_list;
+                       wsi->pending_read_list_prev = NULL;
+                       context->pending_read_list = wsi;
                }
-               
+
                return n;
        }
        n = SSL_get_error(wsi->ssl, n);