Commit 5226865c authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

winhttp: Cache and reuse persistent HTTP connections.

parent e0e8a3ff
...@@ -772,6 +772,36 @@ DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value ) ...@@ -772,6 +772,36 @@ DWORD netconn_set_timeout( netconn_t *netconn, BOOL send, int value )
return ERROR_SUCCESS; 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 ) static DWORD resolve_hostname( const WCHAR *hostnameW, INTERNET_PORT port, struct sockaddr_storage *sa )
{ {
char *hostname; char *hostname;
......
...@@ -1011,15 +1011,25 @@ void release_host( host_t *host ) ...@@ -1011,15 +1011,25 @@ void release_host( host_t *host )
LeaveCriticalSection( &connection_pool_cs ); LeaveCriticalSection( &connection_pool_cs );
if (ref) return; if (ref) return;
assert( list_empty( &host->connections ) );
heap_free( host->hostname ); heap_free( host->hostname );
heap_free( host ); 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 ) static BOOL open_connection( request_t *request )
{ {
BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE; BOOL is_secure = request->hdr.flags & WINHTTP_FLAG_SECURE;
host_t *host = NULL, *iter; host_t *host = NULL, *iter;
netconn_t *netconn; netconn_t *netconn = NULL;
connect_t *connect; connect_t *connect;
WCHAR *addressW = NULL; WCHAR *addressW = NULL;
INTERNET_PORT port; INTERNET_PORT port;
...@@ -1048,6 +1058,7 @@ static BOOL open_connection( request_t *request ) ...@@ -1048,6 +1058,7 @@ static BOOL open_connection( request_t *request )
host->ref = 1; host->ref = 1;
host->secure = is_secure; host->secure = is_secure;
host->port = port; host->port = port;
list_init( &host->connections );
if ((host->hostname = strdupW( connect->servername ))) if ((host->hostname = strdupW( connect->servername )))
{ {
list_add_head( &connection_pool, &host->entry ); list_add_head( &connection_pool, &host->entry );
...@@ -1063,6 +1074,29 @@ static BOOL open_connection( request_t *request ) ...@@ -1063,6 +1074,29 @@ static BOOL open_connection( request_t *request )
if (!host) return FALSE; 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) if (!connect->resolved)
{ {
len = strlenW( host->hostname ) + 1; len = strlenW( host->hostname ) + 1;
...@@ -1084,47 +1118,57 @@ static BOOL open_connection( request_t *request ) ...@@ -1084,47 +1118,57 @@ static BOOL open_connection( request_t *request )
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len ); send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_NAME_RESOLVED, addressW, len );
} }
if (!addressW && !(addressW = addr_to_str( &connect->sockaddr ))) if (!netconn)
{ {
release_host( host ); if (!addressW && !(addressW = addr_to_str( &connect->sockaddr )))
return FALSE; {
} 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 = 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 (!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 ); heap_free( addressW );
netconn_close( netconn ); netconn_close( netconn );
return FALSE; return FALSE;
} }
} }
if (!netconn_secure_connect( netconn, connect->hostname, request->security_flags ))
{ request->netconn = netconn;
netconn_close( netconn ); send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER, addressW, strlenW(addressW) + 1 );
heap_free( addressW );
return FALSE;
}
} }
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: done:
request->read_pos = request->read_size = 0; request->read_pos = request->read_size = 0;
...@@ -1309,6 +1353,8 @@ static void finished_reading( request_t *request ) ...@@ -1309,6 +1353,8 @@ static void finished_reading( request_t *request )
WCHAR connection[20]; WCHAR connection[20];
DWORD size = sizeof(connection); DWORD size = sizeof(connection);
if (!request->netconn) return;
if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE; if (request->hdr.disable_flags & WINHTTP_DISABLE_KEEP_ALIVE) close = TRUE;
else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) || else if (query_headers( request, WINHTTP_QUERY_CONNECTION, NULL, connection, &size, NULL ) ||
query_headers( request, WINHTTP_QUERY_PROXY_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 ) ...@@ -1316,7 +1362,14 @@ static void finished_reading( request_t *request )
if (!strcmpiW( connection, closeW )) close = TRUE; if (!strcmpiW( connection, closeW )) close = TRUE;
} }
else if (!strcmpW( request->version, http1_0 )) 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 */ /* return the size of data available to be read immediately */
......
...@@ -484,8 +484,6 @@ static const struct notification async_test[] = ...@@ -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_RECEIVING_RESPONSE, NF_ALLOW },
{ winhttp_read_data, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW }, { winhttp_read_data, WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED, NF_ALLOW },
{ winhttp_read_data, WINHTTP_CALLBACK_STATUS_READ_COMPLETE, NF_SIGNAL }, { 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 }, { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING },
{ winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL } { winhttp_close_handle, WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, NF_SIGNAL }
......
...@@ -102,6 +102,7 @@ typedef struct { ...@@ -102,6 +102,7 @@ typedef struct {
WCHAR *hostname; WCHAR *hostname;
INTERNET_PORT port; INTERNET_PORT port;
BOOL secure; BOOL secure;
struct list connections;
} host_t; } host_t;
typedef struct typedef struct
...@@ -137,6 +138,7 @@ typedef struct ...@@ -137,6 +138,7 @@ typedef struct
typedef struct typedef struct
{ {
struct list entry;
int socket; int socket;
struct sockaddr_storage sockaddr; struct sockaddr_storage sockaddr;
BOOL secure; /* SSL active on connection? */ BOOL secure; /* SSL active on connection? */
...@@ -300,6 +302,7 @@ BOOL netconn_resolve( WCHAR *, INTERNET_PORT, struct sockaddr_storage *, int ) D ...@@ -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_secure_connect( netconn_t *, WCHAR *, DWORD ) DECLSPEC_HIDDEN;
BOOL netconn_send( netconn_t *, const void *, size_t, int * ) DECLSPEC_HIDDEN; BOOL netconn_send( netconn_t *, const void *, size_t, int * ) DECLSPEC_HIDDEN;
DWORD netconn_set_timeout( netconn_t *, BOOL, 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; const void *netconn_get_certificate( netconn_t * ) DECLSPEC_HIDDEN;
int netconn_get_cipher_strength( netconn_t * ) DECLSPEC_HIDDEN; int netconn_get_cipher_strength( netconn_t * ) DECLSPEC_HIDDEN;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment