Jacek Caban : winhttp: Cache and reuse persistent HTTP connections.

Alexandre Julliard julliard at winehq.org
Tue Jul 18 13:58:54 CDT 2017


Module: wine
Branch: master
Commit: 5226865c5324f366ad6974ddb8291adc20759019
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=5226865c5324f366ad6974ddb8291adc20759019

Author: Jacek Caban <jacek at codeweavers.com>
Date:   Tue Jul 18 00:25:15 2017 +0200

winhttp: Cache and reuse persistent HTTP connections.

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/winhttp/net.c                |  30 ++++++++++
 dlls/winhttp/request.c            | 113 ++++++++++++++++++++++++++++----------
 dlls/winhttp/tests/notification.c |   2 -
 dlls/winhttp/winhttp_private.h    |   3 +
 4 files changed, 116 insertions(+), 32 deletions(-)

diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c
index c57d3af..8c60329 100644
--- a/dlls/winhttp/net.c
+++ b/dlls/winhttp/net.c
@@ -772,6 +772,36 @@ DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )
     return ERROR_SUCCESS;
 }
 
+BOOL netconn_is_alive( netconn_t *netconn )
+{
+#ifdef MSG_DONTWAIT
+    ssize_t len;
+    BYTE b;
+
+    len = recv( netconn->socket, &b, 1, MSG_PEEK | MSG_DONTWAIT );
+    return len == 1 || (len == -1 && errno == EWOULDBLOCK);
+#elif defined(__MINGW32__) || defined(_MSC_VER)
+    ULONG mode;
+    int len;
+    char b;
+
+    mode = 1;
+    if(!ioctlsocket(netconn->socket, FIONBIO, &mode))
+        return FALSE;
+
+    len = recv(netconn->socket, &b, 1, MSG_PEEK);
+
+    mode = 0;
+    if(!ioctlsocket(netconn->socket, FIONBIO, &mode))
+        return FALSE;
+
+    return len == 1 || (len == -1 && WSAGetLastError() == WSAEWOULDBLOCK);
+#else
+    FIXME("not supported on this platform\n");
+    return TRUE;
+#endif
+}
+
 static DWORD resolve_hostname( const WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_storage *sa )
 {
     char *hostname;
diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c
index 97e4ce0..a9f44eaf 100644
--- a/dlls/winhttp/request.c
+++ b/dlls/winhttp/request.c
@@ -1011,15 +1011,25 @@ void release_host( host_t *host )
     LeaveCriticalSection( &connection_pool_cs );
     if (ref) return;
 
+    assert( list_empty( &host->connections ) );
     heap_free( host->hostname );
     heap_free( host );
 }
 
+static void cache_connection( netconn_t *netconn )
+{
+    TRACE( "caching connection %p\n", netconn );
+
+    EnterCriticalSection( &connection_pool_cs );
+    list_add_head( &netconn->host->connections, &netconn->entry );
+    LeaveCriticalSection( &connection_pool_cs );
+}
+
 static BOOL open_connection( request_t *request )
 {
     BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
     host_t *host = NULL, *iter;
-    netconn_t *netconn;
+    netconn_t *netconn = NULL;
     connect_t *connect;
     WCHAR *addressW = NULL;
     INTERNET_PORT port;
@@ -1048,6 +1058,7 @@ static BOOL open_connection( request_t *request )
         host->ref = 1;
         host->secure = is_secure;
         host->port = port;
+        list_init( &host->connections );
         if ((host->hostname = strdupW( connect->servername )))
         {
             list_add_head( &connection_pool, &host->entry );
@@ -1063,6 +1074,29 @@ static BOOL open_connection( request_t *request )
 
     if (!host) return FALSE;
 
+    for (;;)
+    {
+        EnterCriticalSection( &connection_pool_cs );
+        if (!list_empty( &host->connections ))
+        {
+            netconn = LIST_ENTRY( list_head( &host->connections ), netconn_t, entry );
+            list_remove( &netconn->entry );
+        }
+        LeaveCriticalSection( &connection_pool_cs );
+        if (!netconn) break;
+
+        if (netconn_is_alive( netconn )) break;
+        TRACE("connection %p no longer alive, closing\n", netconn);
+        netconn_close( netconn );
+        netconn = NULL;
+    }
+
+    if (!connect->resolved && netconn)
+    {
+        connect->sockaddr = netconn->sockaddr;
+        connect->resolved = TRUE;
+    }
+
     if (!connect->resolved)
     {
         len = strlenW( host->hostname ) + 1;
@@ -1084,47 +1118,57 @@ static BOOL open_connection( request_t *request )
         send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
     }
 
-    if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
+    if (!netconn)
     {
-        release_host( host );
-        return FALSE;
-    }
+        if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
+        {
+            release_host( host );
+            return FALSE;
+        }
 
-    TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
+        TRACE("connecting to %s:%u\n", debugstr_w(addressW), port);
 
-    send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
+        send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 );
 
-    netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout );
-    if (!netconn)
-    {
-        release_host( host );
-        heap_free( addressW );
-        return FALSE;
-    }
-    netconn_set_timeout( netconn, TRUE, request->send_timeout );
-    netconn_set_timeout( netconn, FALSE, request->recv_timeout );
-    if (request->hdr.flags & WINHTTP_FLAG_SECURE)
-    {
-        if (connect->session->proxy_server &&
-            strcmpiW( connect->hostname, connect->servername ))
+        if (!(netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout )))
         {
-            if (!secure_proxy_connect( request ))
+            heap_free( addressW );
+            release_host( host );
+            return FALSE;
+        }
+        netconn_set_timeout( netconn, TRUE, request->send_timeout );
+        netconn_set_timeout( netconn, FALSE, request->recv_timeout );
+        if (is_secure)
+        {
+            if (connect->session->proxy_server &&
+                strcmpiW( connect->hostname, connect->servername ))
+            {
+                if (!secure_proxy_connect( request ))
+                {
+                    heap_free( addressW );
+                    netconn_close( netconn );
+                    return FALSE;
+                }
+            }
+            if (!netconn_secure_connect( netconn, connect->hostname, request->security_flags ))
             {
                 heap_free( addressW );
                 netconn_close( netconn );
                 return FALSE;
             }
         }
-        if (!netconn_secure_connect( netconn, connect->hostname, request->security_flags ))
-        {
-            netconn_close( netconn );
-            heap_free( addressW );
-            return FALSE;
-        }
+
+        request->netconn = netconn;
+        send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 );
     }
-    request->netconn = netconn;
+    else
+    {
+        TRACE("using connection %p\n", netconn);
 
-    send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 );
+        netconn_set_timeout( netconn, TRUE, request->send_timeout );
+        netconn_set_timeout( netconn, FALSE, request->recv_timeout );
+        request->netconn = netconn;
+    }
 
 done:
     request->read_pos = request->read_size = 0;
@@ -1309,6 +1353,8 @@ static void finished_reading( request_t *request )
     WCHAR connection[20];
     DWORD size = sizeof(connection);
 
+    if (!request->netconn) return;
+
     if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
     else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
              query_headers( request, WINHTTP_QUERY_PROXY_CONNECTION, NULL, connection, &size, NULL ))
@@ -1316,7 +1362,14 @@ static void finished_reading( request_t *request )
         if (!strcmpiW( connection, closeW )) close = TRUE;
     }
     else if (!strcmpW( request->version, http1_0 )) close = TRUE;
-    if (close) close_connection( request );
+    if (close)
+    {
+        close_connection( request );
+        return;
+    }
+
+    cache_connection( request->netconn );
+    request->netconn = NULL;
 }
 
 /* return the size of data available to be read immediately */
diff --git a/dlls/winhttp/tests/notification.c b/dlls/winhttp/tests/notification.c
index e9de152..15fabcd8 100644
--- a/dlls/winhttp/tests/notification.c
+++ b/dlls/winhttp/tests/notification.c
@@ -484,8 +484,6 @@ static const struct notification async_test[] =
     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE, NF_ALLOW },
     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
     { winhttp_read_data,        WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL },
-    { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION, NF_WINE_ALLOW },
-    { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED, NF_WINE_ALLOW },
     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
     { winhttp_close_handle,     WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h
index 2b171bf..6d27f22 100644
--- a/dlls/winhttp/winhttp_private.h
+++ b/dlls/winhttp/winhttp_private.h
@@ -102,6 +102,7 @@ typedef struct {
     WCHAR *hostname;
     INTERNET_PORT port;
     BOOL secure;
+    struct list connections;
 } host_t;
 
 typedef struct
@@ -137,6 +138,7 @@ typedef struct
 
 typedef struct
 {
+    struct list entry;
     int socket;
     struct sockaddr_storage sockaddr;
     BOOL secure; /* SSL active on connection? */
@@ -300,6 +302,7 @@ BOOL netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr_storage *, int ) D
 BOOL netconn_secure_connect( netconn_t *, WCHAR *, DWORD ) DECLSPEC_HIDDEN;
 BOOL netconn_send( netconn_t *, const void *, size_t, int * ) DECLSPEC_HIDDEN;
 DWORD netconn_set_timeout( netconn_t *, BOOL, int ) DECLSPEC_HIDDEN;
+BOOL netconn_is_alive( netconn_t * ) DECLSPEC_HIDDEN;
 const void *netconn_get_certificate( netconn_t * ) DECLSPEC_HIDDEN;
 int netconn_get_cipher_strength( netconn_t * ) DECLSPEC_HIDDEN;
 




More information about the wine-cvs mailing list