From 5226865c5324f366ad6974ddb8291adc20759019 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Tue, 18 Jul 2017 00:25:15 +0200 Subject: [PATCH] winhttp: Cache and reuse persistent HTTP connections. Signed-off-by: Jacek Caban Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/winhttp/net.c | 30 ++++++++ dlls/winhttp/request.c | 119 +++++++++++++++++++++--------- dlls/winhttp/tests/notification.c | 2 - dlls/winhttp/winhttp_private.h | 3 + 4 files changed, 119 insertions(+), 35 deletions(-) diff --git a/dlls/winhttp/net.c b/dlls/winhttp/net.c index c57d3afb79e..8c603291b20 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 97e4ce00f17..a9f44eaf7d1 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 ))) - { - release_host( host ); - return FALSE; - } - - TRACE("connecting to %s:%u\n", debugstr_w(addressW), port); - - 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 (!addressW && !(addressW = addr_to_str( &connect->sockaddr ))) { - if (!secure_proxy_connect( request )) + release_host( host ); + return FALSE; + } + + TRACE("connecting to %s:%u\n", debugstr_w(addressW), port); + + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER, addressW, 0 ); + + if (!(netconn = netconn_create( host, &connect->sockaddr, request->connect_timeout ))) + { + 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; + send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 ); + } + else + { + TRACE("using connection %p\n", netconn); + + 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 e9de1520070..15fabcd8c37 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 2b171bf0af6..6d27f22768b 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;