Commit 8a1df203 authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

wininet: Added support for persistent HTTP connections.

parent 28c8e228
......@@ -574,7 +574,7 @@ static void call_continue(PROTOCOLDATA *protocol_data)
CLEAR_CALLED(ReportProgress_FINDINGRESOURCE);
CLEAR_CALLED(ReportProgress_CONNECTING);
CLEAR_CALLED(ReportProgress_PROXYDETECTING);
}else todo_wine {
}else {
CHECK_NOT_CALLED(ReportProgress_FINDINGRESOURCE);
/* IE7 does call this */
CLEAR_CALLED(ReportProgress_CONNECTING);
......
......@@ -1795,7 +1795,7 @@ static HRESULT WINAPI statusclb_OnStopBinding(IBindStatusCallbackEx *iface, HRES
if(filedwl_api)
ok(SUCCEEDED(hresult), "binding failed: %08x\n", hresult);
else if(invalid_cn_accepted)
todo_wine ok(hresult == binding_hres, "binding failed: %08x, expected %08x\n", hresult, binding_hres);
ok(hresult == binding_hres, "binding failed: %08x, expected %08x\n", hresult, binding_hres);
else
ok(hresult == binding_hres, "binding failed: %08x, expected %08x\n", hresult, binding_hres);
ok(szError == NULL, "szError should be NULL\n");
......@@ -2876,14 +2876,12 @@ static void test_BindToStorage(int protocol, DWORD flags, DWORD t)
ok(hres == binding_hres, "Got %08x\n", hres);
ok(unk == NULL, "Got %p\n", unk);
}else if((flags & BINDTEST_INVALID_CN) && invalid_cn_accepted) {
todo_wine {
ok(hres == S_OK, "IMoniker_BindToStorage failed: %08x\n", hres);
ok(unk != NULL, "unk == NULL\n");
if(unk == NULL) {
ok(0, "Expected security problem to be ignored.\n");
invalid_cn_accepted = FALSE;
binding_hres = INET_E_INVALID_CERTIFICATE;
}
ok(hres == S_OK, "IMoniker_BindToStorage failed: %08x\n", hres);
ok(unk != NULL, "unk == NULL\n");
if(unk == NULL) {
ok(0, "Expected security problem to be ignored.\n");
invalid_cn_accepted = FALSE;
binding_hres = INET_E_INVALID_CERTIFICATE;
}
}else {
ok(hres == S_OK, "IMoniker_BindToStorage failed: %08x\n", hres);
......@@ -2958,11 +2956,9 @@ static void test_BindToStorage(int protocol, DWORD flags, DWORD t)
CLEAR_CALLED(OnProgress_CONNECTING);
}
}else if(!abort_start) {
todo_wine {
CHECK_NOT_CALLED(OnProgress_FINDINGRESOURCE);
/* IE7 does call this */
CLEAR_CALLED(OnProgress_CONNECTING);
}
CHECK_NOT_CALLED(OnProgress_FINDINGRESOURCE);
/* IE7 does call this */
CLEAR_CALLED(OnProgress_CONNECTING);
}
if((flags & BINDTEST_INVALID_CN) && !invalid_cn_accepted) {
CHECK_CALLED(QueryInterface_IHttpSecurity);
......@@ -3186,7 +3182,7 @@ static void test_BindToObject(int protocol, DWORD flags)
if(http_is_first) {
CHECK_CALLED(Obj_OnProgress_FINDINGRESOURCE);
CHECK_CALLED(Obj_OnProgress_CONNECTING);
}else todo_wine {
}else {
CHECK_NOT_CALLED(Obj_OnProgress_FINDINGRESOURCE);
/* IE7 does call this */
CLEAR_CALLED(Obj_OnProgress_CONNECTING);
......
......@@ -155,6 +155,8 @@ static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e',
#define HTTP_ADDHDR_FLAG_REPLACE 0x80000000
#define HTTP_ADDHDR_FLAG_REQ 0x02000000
#define COLLECT_TIME 60000
#define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
struct HttpAuthInfo
......@@ -207,7 +209,6 @@ static CRITICAL_SECTION_DEBUG critsect_debug =
};
static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
static DWORD HTTP_OpenConnection(http_request_t *req);
static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
......@@ -219,8 +220,123 @@ static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWOR
static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
static void HTTP_DrainContent(http_request_t *req);
static BOOL HTTP_FinishedReading(http_request_t *req);
static CRITICAL_SECTION connection_pool_cs;
static CRITICAL_SECTION_DEBUG connection_pool_debug =
{
0, 0, &connection_pool_cs,
{ &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
0, 0, { (DWORD_PTR)(__FILE__ ": connection_pool_cs") }
};
static CRITICAL_SECTION connection_pool_cs = { &connection_pool_debug, -1, 0, 0, 0, 0 };
static struct list connection_pool = LIST_INIT(connection_pool);
static BOOL collector_running;
void server_addref(server_t *server)
{
InterlockedIncrement(&server->ref);
}
void server_release(server_t *server)
{
if(InterlockedDecrement(&server->ref))
return;
if(!server->ref)
server->keep_until = GetTickCount64() + COLLECT_TIME;
}
static server_t *get_server(const WCHAR *name, INTERNET_PORT port)
{
server_t *iter, *server = NULL;
EnterCriticalSection(&connection_pool_cs);
LIST_FOR_EACH_ENTRY(iter, &connection_pool, server_t, entry) {
if(iter->port == port && !strcmpW(iter->name, name)) {
server = iter;
server_addref(server);
break;
}
}
if(!server) {
server = heap_alloc(sizeof(*server));
if(server) {
server->addr_len = 0;
server->ref = 1;
server->port = port;
list_init(&server->conn_pool);
server->name = heap_strdupW(name);
if(server->name) {
list_add_head(&connection_pool, &server->entry);
}else {
heap_free(server);
server = NULL;
}
}
}
LeaveCriticalSection(&connection_pool_cs);
return server;
}
BOOL collect_connections(BOOL collect_all)
{
netconn_t *netconn, *netconn_safe;
server_t *server, *server_safe;
BOOL remaining = FALSE;
DWORD64 now;
now = GetTickCount64();
LIST_FOR_EACH_ENTRY_SAFE(server, server_safe, &connection_pool, server_t, entry) {
LIST_FOR_EACH_ENTRY_SAFE(netconn, netconn_safe, &server->conn_pool, netconn_t, pool_entry) {
if(collect_all || netconn->keep_until < now) {
TRACE("freeing %p\n", netconn);
list_remove(&netconn->pool_entry);
free_netconn(netconn);
}else {
remaining = TRUE;
}
}
if(!server->ref) {
if(collect_all || server->keep_until < now) {
list_remove(&server->entry);
heap_free(server->name);
heap_free(server);
}else {
remaining = TRUE;
}
}
}
return remaining;
}
static DWORD WINAPI collect_connections_proc(void *arg)
{
BOOL remaining_conns;
do {
/* FIXME: Use more sophisticated method */
Sleep(5000);
EnterCriticalSection(&connection_pool_cs);
remaining_conns = collect_connections(FALSE);
if(!remaining_conns)
collector_running = FALSE;
LeaveCriticalSection(&connection_pool_cs);
}while(remaining_conns);
FreeLibraryAndExitThread(WININET_hModule, 0);
}
static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
{
......@@ -242,6 +358,7 @@ struct data_stream_vtbl_t {
DWORD (*get_avail_data)(data_stream_t*,http_request_t*);
BOOL (*end_of_data)(data_stream_t*,http_request_t*);
DWORD (*read)(data_stream_t*,http_request_t*,BYTE*,DWORD,DWORD*,read_mode_t);
BOOL (*drain_content)(data_stream_t*,http_request_t*);
void (*destroy)(data_stream_t*);
};
......@@ -357,6 +474,12 @@ static DWORD gzip_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DW
return res;
}
static BOOL gzip_drain_content(data_stream_t *stream, http_request_t *req)
{
gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
return gzip_stream->parent_stream->vtbl->drain_content(gzip_stream->parent_stream, req);
}
static void gzip_destroy(data_stream_t *stream)
{
gzip_stream_t *gzip_stream = (gzip_stream_t*)stream;
......@@ -372,6 +495,7 @@ static const data_stream_vtbl_t gzip_stream_vtbl = {
gzip_get_avail_data,
gzip_end_of_data,
gzip_read,
gzip_drain_content,
gzip_destroy
};
......@@ -1603,44 +1727,42 @@ static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *session, http_req
return TRUE;
}
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif
static DWORD HTTP_ResolveName(http_request_t *request)
static DWORD HTTP_ResolveName(http_request_t *request, server_t *server)
{
char szaddr[INET6_ADDRSTRLEN];
http_session_t *session = request->session;
socklen_t addr_len;
const void *addr;
if(server->addr_len)
return ERROR_SUCCESS;
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_RESOLVING_NAME,
session->serverName,
(strlenW(session->serverName)+1) * sizeof(WCHAR));
server->name,
(strlenW(server->name)+1) * sizeof(WCHAR));
session->sa_len = sizeof(session->socketAddress);
if (!GetAddress(session->serverName, session->serverPort,
(struct sockaddr *)&session->socketAddress, &session->sa_len))
addr_len = sizeof(server->addr);
if (!GetAddress(server->name, server->port, (struct sockaddr *)&server->addr, &addr_len))
return ERROR_INTERNET_NAME_NOT_RESOLVED;
switch (session->socketAddress.ss_family)
{
switch(server->addr.ss_family) {
case AF_INET:
addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
addr = &((struct sockaddr_in *)&server->addr)->sin_addr;
break;
case AF_INET6:
addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
addr = &((struct sockaddr_in6 *)&server->addr)->sin6_addr;
break;
default:
WARN("unsupported family %d\n", session->socketAddress.ss_family);
WARN("unsupported family %d\n", server->addr.ss_family);
return ERROR_INTERNET_NAME_NOT_RESOLVED;
}
inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
server->addr_len = addr_len;
inet_ntop(server->addr.ss_family, addr, server->addr_str, sizeof(server->addr_str));
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_NAME_RESOLVED,
szaddr, strlen(szaddr)+1);
server->addr_str, strlen(server->addr_str)+1);
TRACE("resolved %s to %s\n", debugstr_w(session->serverName), szaddr);
TRACE("resolved %s to %s\n", debugstr_w(server->name), server->addr_str);
return ERROR_SUCCESS;
}
......@@ -1721,24 +1843,72 @@ static void HTTPREQ_Destroy(object_header_t *hdr)
HeapFree(GetProcessHeap(), 0, request->custHeaders);
}
static void HTTPREQ_CloseConnection(object_header_t *hdr)
static void http_release_netconn(http_request_t *req, BOOL reuse)
{
http_request_t *request = (http_request_t*) hdr;
TRACE("%p %p\n",req, req->netconn);
if(!req->netconn)
return;
if(reuse && req->netconn->keep_alive) {
BOOL run_collector;
EnterCriticalSection(&connection_pool_cs);
TRACE("%p\n",request);
list_add_head(&req->netconn->server->conn_pool, &req->netconn->pool_entry);
req->netconn->keep_until = GetTickCount64() + COLLECT_TIME;
req->netconn = NULL;
if (!NETCON_connected(&request->netConnection))
run_collector = !collector_running;
collector_running = TRUE;
LeaveCriticalSection(&connection_pool_cs);
if(run_collector) {
HANDLE thread = NULL;
HMODULE module;
GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR*)WININET_hModule, &module);
if(module)
thread = CreateThread(NULL, 0, collect_connections_proc, NULL, 0, NULL);
if(!thread) {
EnterCriticalSection(&connection_pool_cs);
collector_running = FALSE;
LeaveCriticalSection(&connection_pool_cs);
if(module)
FreeLibrary(module);
}
}
return;
}
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
NETCON_close(&request->netConnection);
free_netconn(req->netconn);
req->netconn = NULL;
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
}
static void drain_content(http_request_t *req)
{
BOOL try_reuse;
if (!req->netconn) return;
if (req->contentLength == -1)
try_reuse = FALSE;
else if(!strcmpW(req->verb, szHEAD))
try_reuse = TRUE;
else
try_reuse = req->data_stream->vtbl->drain_content(req->data_stream, req);
http_release_netconn(req, try_reuse);
}
static BOOL HTTP_KeepAlive(http_request_t *request)
{
WCHAR szVersion[10];
......@@ -1764,6 +1934,13 @@ static BOOL HTTP_KeepAlive(http_request_t *request)
return keepalive;
}
static void HTTPREQ_CloseConnection(object_header_t *hdr)
{
http_request_t *req = (http_request_t*)hdr;
drain_content(req);
}
static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
{
http_request_t *req = (http_request_t*)hdr;
......@@ -1791,7 +1968,7 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
info->Flags |= IDSI_FLAG_KEEP_ALIVE;
if (session->appInfo->proxy && session->appInfo->proxy[0] != 0)
info->Flags |= IDSI_FLAG_PROXY;
if (req->netConnection.useSSL)
if (req->netconn->useSSL)
info->Flags |= IDSI_FLAG_SECURE;
return ERROR_SUCCESS;
......@@ -1800,7 +1977,6 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
case INTERNET_OPTION_SECURITY_FLAGS:
{
DWORD flags;
int bits;
if (*size < sizeof(ULONG))
return ERROR_INSUFFICIENT_BUFFER;
......@@ -1809,14 +1985,16 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
flags = 0;
if (req->hdr.dwFlags & INTERNET_FLAG_SECURE)
flags |= SECURITY_FLAG_SECURE;
flags |= req->netConnection.security_flags;
bits = NETCON_GetCipherStrength(&req->netConnection);
if (bits >= 128)
flags |= SECURITY_FLAG_STRENGTH_STRONG;
else if (bits >= 56)
flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
else
flags |= SECURITY_FLAG_STRENGTH_WEAK;
flags |= req->security_flags;
if(req->netconn) {
int bits = NETCON_GetCipherStrength(req->netconn);
if (bits >= 128)
flags |= SECURITY_FLAG_STRENGTH_STRONG;
else if (bits >= 56)
flags |= SECURITY_FLAG_STRENGTH_MEDIUM;
else
flags |= SECURITY_FLAG_STRENGTH_WEAK;
}
*(DWORD *)buffer = flags;
return ERROR_SUCCESS;
}
......@@ -1940,7 +2118,7 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
return ERROR_INSUFFICIENT_BUFFER;
}
context = (PCCERT_CONTEXT)NETCON_GetCert(&(req->netConnection));
context = (PCCERT_CONTEXT)NETCON_GetCert(req->netconn);
if(context) {
INTERNET_CERTIFICATE_INFOA *info = (INTERNET_CERTIFICATE_INFOA*)buffer;
DWORD len;
......@@ -1962,7 +2140,7 @@ static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffe
CertNameToStrA(context->dwCertEncodingType,
&context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR,
info->lpszIssuerInfo, len);
info->dwKeySize = NETCON_GetCipherStrength(&req->netConnection);
info->dwKeySize = NETCON_GetCipherStrength(req->netconn);
CertFreeCertificateContext(context);
return ERROR_SUCCESS;
}
......@@ -1985,7 +2163,9 @@ static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer,
return ERROR_INVALID_PARAMETER;
flags = *(DWORD *)buffer;
TRACE("%08x\n", flags);
req->netConnection.security_flags = flags;
req->security_flags = flags;
if(req->netconn)
req->netconn->security_flags = flags;
return ERROR_SUCCESS;
}
case INTERNET_OPTION_SEND_TIMEOUT:
......@@ -1995,12 +2175,12 @@ static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer,
if (size != sizeof(DWORD))
return ERROR_INVALID_PARAMETER;
if(NETCON_connected(&req->netConnection)) {
if(!req->netconn) {
FIXME("unsupported without active connection\n");
return ERROR_SUCCESS;
}
return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
return NETCON_set_timeout(req->netconn, option == INTERNET_OPTION_SEND_TIMEOUT,
*(DWORD*)buffer);
case INTERNET_OPTION_USERNAME:
......@@ -2038,7 +2218,7 @@ static DWORD read_more_data( http_request_t *req, int maxlen )
if (maxlen == -1) maxlen = sizeof(req->read_buf);
res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
res = NETCON_recv( req->netconn, req->read_buf + req->read_size,
maxlen - req->read_size, 0, &len );
if(res == ERROR_SUCCESS)
req->read_size += len;
......@@ -2079,7 +2259,7 @@ static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
{
*len = 0;
TRACE( "returning empty string\n" );
TRACE( "returning empty string %u\n", res);
LeaveCriticalSection( &req->read_section );
INTERNET_SetLastError(res);
return FALSE;
......@@ -2133,16 +2313,20 @@ static DWORD get_avail_data( http_request_t *req )
static DWORD netconn_get_avail_data(data_stream_t *stream, http_request_t *req)
{
netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
DWORD avail = 0;
NETCON_query_data_available(&req->netConnection, &avail);
return avail;
if(req->netconn)
NETCON_query_data_available(req->netconn, &avail);
return netconn_stream->content_length == ~0u
? avail
: min(avail, netconn_stream->content_length-netconn_stream->content_read);
}
static BOOL netconn_end_of_data(data_stream_t *stream, http_request_t *req)
{
netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
return netconn_stream->content_read == netconn_stream->content_length || !NETCON_connected(&req->netConnection);
return netconn_stream->content_read == netconn_stream->content_length || !req->netconn;
}
static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf, DWORD size,
......@@ -2156,8 +2340,8 @@ static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf,
if(read_mode == READMODE_NOBLOCK)
size = min(size, netconn_get_avail_data(stream, req));
if(size) {
if(NETCON_recv(&req->netConnection, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
if(size && req->netconn) {
if(NETCON_recv(req->netconn, buf, size, read_mode == READMODE_SYNC ? MSG_WAITALL : 0, &len) != ERROR_SUCCESS)
len = 0;
}
......@@ -2166,6 +2350,30 @@ static DWORD netconn_read(data_stream_t *stream, http_request_t *req, BYTE *buf,
return ERROR_SUCCESS;
}
static BOOL netconn_drain_content(data_stream_t *stream, http_request_t *req)
{
netconn_stream_t *netconn_stream = (netconn_stream_t*)stream;
BYTE buf[1024];
DWORD avail;
int len;
if(netconn_end_of_data(stream, req))
return TRUE;
do {
avail = netconn_get_avail_data(stream, req);
if(!avail)
return FALSE;
if(NETCON_recv(req->netconn, buf, min(avail, sizeof(buf)), 0, &len) != ERROR_SUCCESS)
return FALSE;
netconn_stream->content_read += len;
}while(netconn_stream->content_read < netconn_stream->content_length);
return TRUE;
}
static void netconn_destroy(data_stream_t *stream)
{
}
......@@ -2174,6 +2382,7 @@ static const data_stream_vtbl_t netconn_stream_vtbl = {
netconn_get_avail_data,
netconn_end_of_data,
netconn_read,
netconn_drain_content,
netconn_destroy
};
......@@ -2193,7 +2402,7 @@ static DWORD read_more_chunked_data(chunked_stream_t *stream, http_request_t *re
if (maxlen == -1) maxlen = sizeof(stream->buf);
res = NETCON_recv( &req->netConnection, stream->buf + stream->buf_size,
res = NETCON_recv( req->netconn, stream->buf + stream->buf_size,
maxlen - stream->buf_size, 0, &len );
if(res == ERROR_SUCCESS)
stream->buf_size += len;
......@@ -2302,7 +2511,7 @@ static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf,
if(read_mode == READMODE_NOBLOCK) {
DWORD avail;
if(!NETCON_query_data_available(&req->netConnection, &avail) || !avail)
if(!NETCON_query_data_available(req->netconn, &avail) || !avail)
break;
if(read_bytes > avail)
read_bytes = avail;
......@@ -2312,7 +2521,7 @@ static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf,
break;
}
res = NETCON_recv(&req->netConnection, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
res = NETCON_recv(req->netconn, (char *)buf+ret_read, read_bytes, 0, (int*)&read_bytes);
if(res != ERROR_SUCCESS)
break;
}
......@@ -2336,6 +2545,14 @@ static DWORD chunked_read(data_stream_t *stream, http_request_t *req, BYTE *buf,
return res;
}
static BOOL chunked_drain_content(data_stream_t *stream, http_request_t *req)
{
chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
/* FIXME: we can do better */
return !chunked_stream->chunk_size;
}
static void chunked_destroy(data_stream_t *stream)
{
chunked_stream_t *chunked_stream = (chunked_stream_t*)stream;
......@@ -2346,6 +2563,7 @@ static const data_stream_vtbl_t chunked_stream_vtbl = {
chunked_get_avail_data,
chunked_end_of_data,
chunked_read,
chunked_drain_content,
chunked_destroy
};
......@@ -2464,7 +2682,7 @@ static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *
}
if(end_of_read_data(req))
HTTP_FinishedReading(req);
http_release_netconn(req, TRUE);
return res;
}
......@@ -2710,7 +2928,7 @@ static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD s
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
*written = 0;
res = NETCON_send(&request->netConnection, buffer, size, 0, (LPINT)written);
res = NETCON_send(request->netconn, buffer, size, 0, (LPINT)written);
if (res == ERROR_SUCCESS)
request->bytesWritten += *written;
......@@ -2795,15 +3013,12 @@ static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
{
appinfo_t *hIC = NULL;
appinfo_t *hIC = session->appInfo;
http_request_t *request;
DWORD len, res;
DWORD len, res = ERROR_SUCCESS;
TRACE("-->\n");
assert( session->hdr.htype == WH_HHTTPSESSION );
hIC = session->appInfo;
request = alloc_object(&session->hdr, &HTTPREQVtbl, sizeof(http_request_t));
if(!request)
return ERROR_OUTOFMEMORY;
......@@ -2822,12 +3037,10 @@ static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
request->session = session;
list_add_head( &session->hdr.children, &request->hdr.entry );
if ((res = NETCON_init(&request->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
goto lend;
if (dwFlags & INTERNET_FLAG_IGNORE_CERT_CN_INVALID)
request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
request->security_flags |= SECURITY_FLAG_IGNORE_CERT_CN_INVALID;
if (dwFlags & INTERNET_FLAG_IGNORE_CERT_DATE_INVALID)
request->netConnection.security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
request->security_flags |= SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
if (lpszObjectName && *lpszObjectName) {
HRESULT rc;
......@@ -2901,7 +3114,7 @@ static DWORD HTTP_HttpOpenRequestW(http_session_t *session,
INTERNET_DEFAULT_HTTPS_PORT :
INTERNET_DEFAULT_HTTP_PORT);
if (NULL != hIC->proxy && hIC->proxy[0] != 0)
if (hIC->proxy && hIC->proxy[0])
HTTP_DealWithProxy( hIC, session, request );
INTERNET_SendCallback(&session->hdr, dwContext,
......@@ -2977,29 +3190,6 @@ lend:
return handle;
}
/* read any content returned by the server so that the connection can be
* reused */
static void HTTP_DrainContent(http_request_t *req)
{
DWORD bytes_read;
if (!NETCON_connected(&req->netConnection)) return;
if (req->contentLength == -1)
{
NETCON_close(&req->netConnection);
return;
}
if (!strcmpW(req->verb, szHEAD)) return;
do
{
char buffer[2048];
if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
return;
} while (bytes_read);
}
static const LPCWSTR header_lookup[] = {
szMime_Version, /* HTTP_QUERY_MIME_VERSION = 0 */
szContent_Type, /* HTTP_QUERY_CONTENT_TYPE = 1 */
......@@ -3653,29 +3843,15 @@ static DWORD HTTP_HandleRedirect(http_request_t *request, LPCWSTR lpszUrl)
if (userName[0])
session->userName = heap_strdupW(userName);
if (!using_proxy)
{
if (strcmpiW(session->serverName, hostName) || session->serverPort != urlComponents.nPort)
{
DWORD res;
reset_data_stream(request);
if(!using_proxy) {
if(strcmpiW(session->serverName, hostName)) {
HeapFree(GetProcessHeap(), 0, session->serverName);
session->serverName = heap_strdupW(hostName);
session->serverPort = urlComponents.nPort;
NETCON_close(&request->netConnection);
if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS)
return res;
res = NETCON_init(&request->netConnection, request->hdr.dwFlags & INTERNET_FLAG_SECURE);
if (res != ERROR_SUCCESS)
return res;
reset_data_stream(request);
}
session->serverPort = urlComponents.nPort;
}
else
TRACE("Redirect through proxy\n");
}
HeapFree(GetProcessHeap(), 0, request->path);
......@@ -3762,7 +3938,7 @@ static DWORD HTTP_SecureProxyConnect(http_request_t *request)
TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
res = NETCON_send( &request->netConnection, ascii_req, len, 0, &cnt );
res = NETCON_send( request->netconn, ascii_req, len, 0, &cnt );
HeapFree( GetProcessHeap(), 0, ascii_req );
if (res != ERROR_SUCCESS)
return res;
......@@ -4189,6 +4365,17 @@ static void HTTP_ProcessLastModified(http_request_t *request)
}
}
static void http_process_keep_alive(http_request_t *req)
{
int index;
index = HTTP_GetCustomHeaderIndex(req, szConnection, 0, FALSE);
if(index != -1)
req->netconn->keep_alive = !strcmpiW(req->custHeaders[index].lpszValue, szKeepAlive);
else
req->netconn->keep_alive = !strcmpiW(req->version, g_szHttp1_1);
}
static void HTTP_CacheRequest(http_request_t *request)
{
WCHAR url[INTERNET_MAX_URL_LENGTH];
......@@ -4218,6 +4405,105 @@ static void HTTP_CacheRequest(http_request_t *request)
}
}
static DWORD open_http_connection(http_request_t *request, BOOL *reusing)
{
const BOOL is_https = (request->hdr.dwFlags & INTERNET_FLAG_SECURE) != 0;
http_session_t *session = request->session;
netconn_t *netconn = NULL;
server_t *server;
DWORD res;
assert(!request->netconn);
reset_data_stream(request);
server = get_server(session->serverName, session->serverPort);
if(!server)
return ERROR_OUTOFMEMORY;
res = HTTP_ResolveName(request, server);
if(res != ERROR_SUCCESS) {
server_release(server);
return res;
}
EnterCriticalSection(&connection_pool_cs);
while(!list_empty(&server->conn_pool)) {
netconn = LIST_ENTRY(list_head(&server->conn_pool), netconn_t, pool_entry);
list_remove(&netconn->pool_entry);
if(NETCON_is_alive(netconn))
break;
TRACE("connection %p closed during idle\n", netconn);
free_netconn(netconn);
netconn = NULL;
}
LeaveCriticalSection(&connection_pool_cs);
if(netconn) {
TRACE("<-- reusing %p netconn\n", netconn);
request->netconn = netconn;
*reusing = TRUE;
return ERROR_SUCCESS;
}
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_CONNECTING_TO_SERVER,
server->addr_str,
strlen(server->addr_str)+1);
res = create_netconn(is_https, server, request->security_flags, &netconn);
server_release(server);
if(res != ERROR_SUCCESS) {
ERR("create_netconn failed: %u\n", res);
return res;
}
request->netconn = netconn;
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_CONNECTED_TO_SERVER,
server->addr_str, strlen(server->addr_str)+1);
if(is_https) {
/* Note: we differ from Microsoft's WinINet here. they seem to have
* a bug that causes no status callbacks to be sent when starting
* a tunnel to a proxy server using the CONNECT verb. i believe our
* behaviour to be more correct and to not cause any incompatibilities
* because using a secure connection through a proxy server is a rare
* case that would be hard for anyone to depend on */
if(session->appInfo->proxy)
res = HTTP_SecureProxyConnect(request);
if(res == ERROR_SUCCESS)
res = NETCON_secure_connect(request->netconn, session->hostName);
if(res != ERROR_SUCCESS)
{
WARN("Couldn't connect securely to host\n");
if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
|| res == ERROR_INTERNET_INVALID_CA
|| res == ERROR_INTERNET_SEC_CERT_NO_REV
|| res == ERROR_INTERNET_SEC_CERT_REV_FAILED
|| res == ERROR_INTERNET_SEC_CERT_REVOKED
|| res == ERROR_INTERNET_SEC_INVALID_CERT
|| res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
res = ERROR_INTERNET_SEC_CERT_ERRORS;
}
}
if(res != ERROR_SUCCESS) {
http_release_netconn(request, FALSE);
return res;
}
*reusing = FALSE;
TRACE("Created connection to %s: %p\n", debugstr_w(server->name), netconn);
return ERROR_SUCCESS;
}
/***********************************************************************
* HTTP_HttpSendRequestW (internal)
*
......@@ -4293,7 +4579,7 @@ static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
/* like native, just in case the caller forgot to call InternetReadFile
* for all the data */
HTTP_DrainContent(request);
drain_content(request);
if(redirected) {
request->contentLength = ~0u;
request->bytesToWrite = 0;
......@@ -4335,14 +4621,8 @@ static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
TRACE("Request header -> %s\n", debugstr_w(requestString) );
/* Send the request and store the results */
if(NETCON_connected(&request->netConnection))
reusing_connection = TRUE;
else
reusing_connection = FALSE;
if ((res = HTTP_OpenConnection(request)) != ERROR_SUCCESS)
goto lend;
if ((res = open_http_connection(request, &reusing_connection)) != ERROR_SUCCESS)
break;
/* send the request as ASCII, tack on the optional data */
if (!lpOptional || redirected)
......@@ -4361,8 +4641,16 @@ static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
res = NETCON_send(&request->netConnection, ascii_req, len, 0, &cnt);
res = NETCON_send(request->netconn, ascii_req, len, 0, &cnt);
HeapFree( GetProcessHeap(), 0, ascii_req );
if(res != ERROR_SUCCESS) {
TRACE("send failed: %u\n", res);
if(!reusing_connection)
break;
http_release_netconn(request, FALSE);
loop_next = TRUE;
continue;
}
request->bytesWritten = dwOptionalLength;
......@@ -4378,22 +4666,21 @@ static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
if (res != ERROR_SUCCESS)
goto lend;
responseLen = HTTP_GetResponseHeaders(request, TRUE);
/* FIXME: We should know that connection is closed before sending
* headers. Otherwise wrong callbacks are executed */
if(!responseLen && reusing_connection) {
TRACE("Connection closed by server, reconnecting\n");
http_release_netconn(request, FALSE);
loop_next = TRUE;
continue;
}
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
sizeof(DWORD));
http_process_keep_alive(request);
HTTP_ProcessCookies(request);
HTTP_ProcessExpires(request);
HTTP_ProcessLastModified(request);
......@@ -4402,7 +4689,7 @@ static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
if(res != ERROR_SUCCESS)
goto lend;
if(!request->contentLength)
HTTP_FinishedReading(request);
http_release_netconn(request, TRUE);
dwBufferSize = sizeof(dwStatusCode);
if (HTTP_HttpQueryInfoW(request,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
......@@ -4424,7 +4711,7 @@ static DWORD HTTP_HttpSendRequestW(http_request_t *request, LPCWSTR lpszHeaders,
HeapFree(GetProcessHeap(), 0, request->verb);
request->verb = heap_strdupW(szGET);
}
HTTP_DrainContent(request);
drain_content(request);
if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
{
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
......@@ -4562,13 +4849,14 @@ static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_
INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
/* process cookies here. Is this right? */
http_process_keep_alive(request);
HTTP_ProcessCookies(request);
HTTP_ProcessExpires(request);
HTTP_ProcessLastModified(request);
if ((res = set_content_length( request )) == ERROR_SUCCESS) {
if(!request->contentLength)
HTTP_FinishedReading(request);
http_release_netconn(request, TRUE);
}
if (res == ERROR_SUCCESS && !(request->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
......@@ -4589,7 +4877,7 @@ static DWORD HTTP_HttpEndRequestW(http_request_t *request, DWORD dwFlags, DWORD_
HeapFree(GetProcessHeap(), 0, request->verb);
request->verb = heap_strdupW(szGET);
}
HTTP_DrainContent(request);
drain_content(request);
if ((new_url = HTTP_GetRedirectURL( request, szNewLocation )))
{
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext, INTERNET_STATUS_REDIRECT,
......@@ -5128,117 +5416,6 @@ DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
return ERROR_SUCCESS;
}
/***********************************************************************
* HTTP_OpenConnection (internal)
*
* Connect to a web server
*
* RETURNS
*
* TRUE on success
* FALSE on failure
*/
static DWORD HTTP_OpenConnection(http_request_t *request)
{
http_session_t *session;
appinfo_t *hIC = NULL;
char szaddr[INET6_ADDRSTRLEN];
const void *addr;
DWORD res = ERROR_SUCCESS;
TRACE("-->\n");
if (request->hdr.htype != WH_HHTTPREQ)
{
res = ERROR_INVALID_PARAMETER;
goto lend;
}
if (NETCON_connected(&request->netConnection))
goto lend;
if ((res = HTTP_ResolveName(request)) != ERROR_SUCCESS) goto lend;
session = request->session;
hIC = session->appInfo;
switch (session->socketAddress.ss_family)
{
case AF_INET:
addr = &((struct sockaddr_in *)&session->socketAddress)->sin_addr;
break;
case AF_INET6:
addr = &((struct sockaddr_in6 *)&session->socketAddress)->sin6_addr;
break;
default:
WARN("unsupported family %d\n", session->socketAddress.ss_family);
return ERROR_INTERNET_NAME_NOT_RESOLVED;
}
inet_ntop(session->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_CONNECTING_TO_SERVER,
szaddr,
strlen(szaddr)+1);
res = NETCON_create(&request->netConnection, session->socketAddress.ss_family, SOCK_STREAM, 0);
if (res != ERROR_SUCCESS)
{
WARN("Socket creation failed: %u\n", res);
goto lend;
}
res = NETCON_connect(&request->netConnection, (struct sockaddr *)&session->socketAddress,
session->sa_len);
if(res != ERROR_SUCCESS)
goto lend;
INTERNET_SendCallback(&request->hdr, request->hdr.dwContext,
INTERNET_STATUS_CONNECTED_TO_SERVER,
szaddr, strlen(szaddr)+1);
if (request->hdr.dwFlags & INTERNET_FLAG_SECURE)
{
/* Note: we differ from Microsoft's WinINet here. they seem to have
* a bug that causes no status callbacks to be sent when starting
* a tunnel to a proxy server using the CONNECT verb. i believe our
* behaviour to be more correct and to not cause any incompatibilities
* because using a secure connection through a proxy server is a rare
* case that would be hard for anyone to depend on */
if (hIC->proxy && (res = HTTP_SecureProxyConnect(request)) != ERROR_SUCCESS) {
HTTPREQ_CloseConnection(&request->hdr);
goto lend;
}
res = NETCON_secure_connect(&request->netConnection, session->hostName);
if(res != ERROR_SUCCESS)
{
WARN("Couldn't connect securely to host\n");
if((request->hdr.ErrorMask&INTERNET_ERROR_MASK_COMBINED_SEC_CERT) && (
res == ERROR_INTERNET_SEC_CERT_DATE_INVALID
|| res == ERROR_INTERNET_INVALID_CA
|| res == ERROR_INTERNET_SEC_CERT_NO_REV
|| res == ERROR_INTERNET_SEC_CERT_REV_FAILED
|| res == ERROR_INTERNET_SEC_CERT_REVOKED
|| res == ERROR_INTERNET_SEC_INVALID_CERT
|| res == ERROR_INTERNET_SEC_CERT_CN_INVALID))
res = ERROR_INTERNET_SEC_CERT_ERRORS;
HTTPREQ_CloseConnection(&request->hdr);
goto lend;
}
}
lend:
reset_data_stream(request);
TRACE("%d <--\n", res);
return res;
}
/***********************************************************************
* HTTP_clear_response_headers (internal)
*
......@@ -5288,7 +5465,7 @@ static INT HTTP_GetResponseHeaders(http_request_t *request, BOOL clear)
TRACE("-->\n");
if (!NETCON_connected(&request->netConnection))
if(!request->netconn)
goto lend;
do {
......@@ -5623,31 +5800,6 @@ static DWORD HTTP_ProcessHeader(http_request_t *request, LPCWSTR field, LPCWSTR
return res;
}
/***********************************************************************
* HTTP_FinishedReading (internal)
*
* Called when all content from server has been read by client.
*
*/
static BOOL HTTP_FinishedReading(http_request_t *request)
{
BOOL keepalive = HTTP_KeepAlive(request);
TRACE("\n");
if (!keepalive)
{
HTTPREQ_CloseConnection(&request->hdr);
}
/* FIXME: store data in the URL cache here */
return TRUE;
}
/***********************************************************************
* HTTP_GetCustomHeaderIndex (internal)
*
......
......@@ -308,7 +308,7 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
break;
case DLL_PROCESS_DETACH:
collect_connections(TRUE);
NETCON_unload();
URLCacheContainers_DeleteAll();
......
......@@ -49,13 +49,40 @@
extern HMODULE WININET_hModule;
#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif
typedef struct {
WCHAR *name;
INTERNET_PORT port;
struct sockaddr_storage addr;
socklen_t addr_len;
char addr_str[INET6_ADDRSTRLEN];
LONG ref;
DWORD64 keep_until;
struct list entry;
struct list conn_pool;
} server_t;
void server_addref(server_t*);
void server_release(server_t*);
BOOL collect_connections(BOOL);
/* used for netconnection.c stuff */
typedef struct
{
BOOL useSSL;
int socketFD;
void *ssl_s;
server_t *server;
DWORD security_flags;
BOOL keep_alive;
DWORD64 keep_until;
struct list pool_entry;
} netconn_t;
static inline void * __WINE_ALLOC_SIZE(1) heap_alloc(size_t len)
......@@ -228,7 +255,6 @@ typedef struct
DWORD accessType;
} appinfo_t;
typedef struct
{
object_header_t hdr;
......@@ -239,8 +265,6 @@ typedef struct
LPWSTR password;
INTERNET_PORT hostPort; /* the final destination port of the request */
INTERNET_PORT serverPort; /* the port of the server we directly connect to */
struct sockaddr_storage socketAddress;
socklen_t sa_len;
} http_session_t;
#define HDR_ISREQUEST 0x0001
......@@ -279,7 +303,8 @@ typedef struct
LPWSTR path;
LPWSTR verb;
LPWSTR rawHeaders;
netconn_t netConnection;
netconn_t *netconn;
DWORD security_flags;
LPWSTR version;
LPWSTR statusText;
DWORD bytesToWrite;
......@@ -487,20 +512,16 @@ VOID INTERNET_SendCallback(object_header_t *hdr, DWORD_PTR dwContext,
DWORD dwStatusInfoLength) DECLSPEC_HIDDEN;
BOOL INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto, WCHAR *foundProxy, DWORD *foundProxyLen) DECLSPEC_HIDDEN;
BOOL NETCON_connected(netconn_t *connection) DECLSPEC_HIDDEN;
DWORD NETCON_init(netconn_t *connnection, BOOL useSSL) DECLSPEC_HIDDEN;
DWORD create_netconn(BOOL,server_t*,DWORD,netconn_t**) DECLSPEC_HIDDEN;
void free_netconn(netconn_t*) DECLSPEC_HIDDEN;
void NETCON_unload(void) DECLSPEC_HIDDEN;
DWORD NETCON_create(netconn_t *connection, int domain,
int type, int protocol) DECLSPEC_HIDDEN;
DWORD NETCON_close(netconn_t *connection) DECLSPEC_HIDDEN;
DWORD NETCON_connect(netconn_t *connection, const struct sockaddr *serv_addr,
unsigned int addrlen) DECLSPEC_HIDDEN;
DWORD NETCON_secure_connect(netconn_t *connection, LPWSTR hostname) DECLSPEC_HIDDEN;
DWORD NETCON_send(netconn_t *connection, const void *msg, size_t len, int flags,
int *sent /* out */) DECLSPEC_HIDDEN;
DWORD NETCON_recv(netconn_t *connection, void *buf, size_t len, int flags,
int *recvd /* out */) DECLSPEC_HIDDEN;
BOOL NETCON_query_data_available(netconn_t *connection, DWORD *available) DECLSPEC_HIDDEN;
BOOL NETCON_is_alive(netconn_t*) DECLSPEC_HIDDEN;
LPCVOID NETCON_GetCert(netconn_t *connection) DECLSPEC_HIDDEN;
int NETCON_GetCipherStrength(netconn_t*) DECLSPEC_HIDDEN;
DWORD NETCON_set_timeout(netconn_t *connection, BOOL send, int value) DECLSPEC_HIDDEN;
......
......@@ -70,6 +70,7 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include "wine/library.h"
#include "windef.h"
......@@ -493,22 +494,64 @@ static DWORD init_openssl(void)
#endif
}
DWORD NETCON_init(netconn_t *connection, BOOL useSSL)
DWORD create_netconn(BOOL useSSL, server_t *server, DWORD security_flags, netconn_t **ret)
{
DWORD res = ERROR_SUCCESS;
netconn_t *netconn;
int result;
connection->useSSL = useSSL;
connection->socketFD = -1;
if(useSSL) {
DWORD res;
if (useSSL) {
TRACE("using SSL connection\n");
EnterCriticalSection(&init_ssl_cs);
res = init_openssl();
LeaveCriticalSection(&init_ssl_cs);
if(res != ERROR_SUCCESS)
return res;
}
return res;
netconn = heap_alloc_zero(sizeof(*netconn));
if(!netconn)
return ERROR_OUTOFMEMORY;
netconn->useSSL = useSSL;
netconn->socketFD = -1;
netconn->security_flags = security_flags;
list_init(&netconn->pool_entry);
assert(server->addr_len);
result = netconn->socketFD = socket(server->addr.ss_family, SOCK_STREAM, 0);
if(result != -1) {
result = connect(netconn->socketFD, (struct sockaddr*)&server->addr, server->addr_len);
if(result == -1)
closesocket(netconn->socketFD);
}
if(result == -1) {
heap_free(netconn);
return sock_get_error(errno);
}
server_addref(server);
netconn->server = server;
*ret = netconn;
return ERROR_SUCCESS;
}
void free_netconn(netconn_t *netconn)
{
server_release(netconn->server);
#ifdef SONAME_LIBSSL
if (netconn->ssl_s) {
pSSL_shutdown(netconn->ssl_s);
pSSL_free(netconn->ssl_s);
}
#endif
closesocket(netconn->socketFD);
heap_free(netconn);
}
void NETCON_unload(void)
......@@ -534,11 +577,6 @@ void NETCON_unload(void)
#endif
}
BOOL NETCON_connected(netconn_t *connection)
{
return connection->socketFD != -1;
}
/* translate a unix error code into a winsock one */
int sock_get_error( int err )
{
......@@ -607,52 +645,6 @@ int sock_get_error( int err )
}
/******************************************************************************
* NETCON_create
* Basically calls 'socket()'
*/
DWORD NETCON_create(netconn_t *connection, int domain,
int type, int protocol)
{
#ifdef SONAME_LIBSSL
if (connection->ssl_s)
return ERROR_NOT_SUPPORTED;
#endif
connection->socketFD = socket(domain, type, protocol);
if (connection->socketFD == -1)
return sock_get_error(errno);
return ERROR_SUCCESS;
}
/******************************************************************************
* NETCON_close
* Basically calls 'close()' unless we should use SSL
*/
DWORD NETCON_close(netconn_t *connection)
{
int result;
if (!NETCON_connected(connection)) return ERROR_SUCCESS;
#ifdef SONAME_LIBSSL
if (connection->ssl_s)
{
pSSL_shutdown(connection->ssl_s);
pSSL_free(connection->ssl_s);
connection->ssl_s = NULL;
}
#endif
result = closesocket(connection->socketFD);
connection->socketFD = -1;
if (result == -1)
return sock_get_error(errno);
return ERROR_SUCCESS;
}
/******************************************************************************
* NETCON_secure_connect
* Initiates a secure connection over an existing plaintext connection.
*/
......@@ -722,28 +714,6 @@ fail:
}
/******************************************************************************
* NETCON_connect
* Connects to the specified address.
*/
DWORD NETCON_connect(netconn_t *connection, const struct sockaddr *serv_addr,
unsigned int addrlen)
{
int result;
result = connect(connection->socketFD, serv_addr, addrlen);
if (result == -1)
{
WARN("Unable to connect to host (%s)\n", strerror(errno));
closesocket(connection->socketFD);
connection->socketFD = -1;
return sock_get_error(errno);
}
return ERROR_SUCCESS;
}
/******************************************************************************
* NETCON_send
* Basically calls 'send()' unless we should use SSL
* number of chars send is put in *sent
......@@ -751,7 +721,6 @@ DWORD NETCON_connect(netconn_t *connection, const struct sockaddr *serv_addr,
DWORD NETCON_send(netconn_t *connection, const void *msg, size_t len, int flags,
int *sent /* out */)
{
if (!NETCON_connected(connection)) return ERROR_INTERNET_CONNECTION_ABORTED;
if (!connection->useSSL)
{
*sent = send(connection->socketFD, msg, len, flags);
......@@ -787,14 +756,11 @@ DWORD NETCON_recv(netconn_t *connection, void *buf, size_t len, int flags,
int *recvd /* out */)
{
*recvd = 0;
if (!NETCON_connected(connection)) return ERROR_INTERNET_CONNECTION_ABORTED;
if (!len)
return ERROR_SUCCESS;
if (!connection->useSSL)
{
*recvd = recv(connection->socketFD, buf, len, flags);
if(!*recvd)
NETCON_close(connection);
return *recvd == -1 ? sock_get_error(errno) : ERROR_SUCCESS;
}
else
......@@ -808,10 +774,8 @@ DWORD NETCON_recv(netconn_t *connection, void *buf, size_t len, int flags,
/* Check if EOF was received */
if(!*recvd && (pSSL_get_error(connection->ssl_s, *recvd)==SSL_ERROR_ZERO_RETURN
|| pSSL_get_error(connection->ssl_s, *recvd)==SSL_ERROR_SYSCALL)) {
NETCON_close(connection);
|| pSSL_get_error(connection->ssl_s, *recvd)==SSL_ERROR_SYSCALL))
return ERROR_SUCCESS;
}
return *recvd > 0 ? ERROR_SUCCESS : ERROR_INTERNET_CONNECTION_ABORTED;
#else
......@@ -828,8 +792,6 @@ DWORD NETCON_recv(netconn_t *connection, void *buf, size_t len, int flags,
BOOL NETCON_query_data_available(netconn_t *connection, DWORD *available)
{
*available = 0;
if (!NETCON_connected(connection))
return FALSE;
if (!connection->useSSL)
{
......@@ -852,6 +814,36 @@ BOOL NETCON_query_data_available(netconn_t *connection, DWORD *available)
return TRUE;
}
BOOL NETCON_is_alive(netconn_t *netconn)
{
#ifdef MSG_DONTWAIT
ssize_t len;
BYTE b;
len = recv(netconn->socketFD, &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->socketFD, FIONBIO, &mode))
return FALSE;
len = recv(netconn->socketFD, &b, 1, MSG_PEEK);
mode = 0;
if(!ioctlsocket(netconn->socketFD, FIONBIO, &mode))
return FALSE;
return len == 1 || (len == -1 && errno == WSAEWOULDBLOCK);
#else
FIXME("not supported on this platform\n");
return TRUE;
#endif
}
LPCVOID NETCON_GetCert(netconn_t *connection)
{
#ifdef SONAME_LIBSSL
......
......@@ -386,17 +386,8 @@ static void InternetReadFile_test(int flags, const test_data_t *test)
{
SET_EXPECT(INTERNET_STATUS_RESOLVING_NAME);
SET_EXPECT(INTERNET_STATUS_NAME_RESOLVED);
SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME);
SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED);
}
else
{
SET_WINE_ALLOW2(INTERNET_STATUS_RESOLVING_NAME,2);
SET_WINE_ALLOW2(INTERNET_STATUS_NAME_RESOLVED,2);
}
SET_WINE_ALLOW(INTERNET_STATUS_CONNECTING_TO_SERVER);
SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER);
SET_WINE_ALLOW(INTERNET_STATUS_CONNECTED_TO_SERVER);
SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER);
SET_EXPECT2(INTERNET_STATUS_SENDING_REQUEST, (test->flags & TESTF_REDIRECT) ? 2 : 1);
SET_EXPECT2(INTERNET_STATUS_REQUEST_SENT, (test->flags & TESTF_REDIRECT) ? 2 : 1);
......@@ -461,7 +452,7 @@ static void InternetReadFile_test(int flags, const test_data_t *test)
CLEAR_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED);
}
}
else todo_wine
else
{
CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME);
CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED);
......@@ -582,8 +573,6 @@ abort:
trace("aborting\n");
SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0));
if (hor != 0x0) {
SET_WINE_ALLOW(INTERNET_STATUS_CLOSING_CONNECTION);
SET_WINE_ALLOW(INTERNET_STATUS_CONNECTION_CLOSED);
SetLastError(0xdeadbeef);
trace("closing\n");
res = InternetCloseHandle(hor);
......@@ -608,7 +597,7 @@ abort:
Sleep(100);
}
CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0));
if (hor != 0x0) todo_wine
if (hor != 0x0)
{
CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION);
CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED);
......@@ -791,17 +780,8 @@ static void InternetReadFileExA_test(int flags)
{
SET_EXPECT(INTERNET_STATUS_RESOLVING_NAME);
SET_EXPECT(INTERNET_STATUS_NAME_RESOLVED);
SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME);
SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED);
}
else
{
SET_WINE_ALLOW2(INTERNET_STATUS_RESOLVING_NAME,2);
SET_WINE_ALLOW2(INTERNET_STATUS_NAME_RESOLVED,2);
}
SET_WINE_ALLOW(INTERNET_STATUS_CONNECTING_TO_SERVER);
SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER);
SET_WINE_ALLOW(INTERNET_STATUS_CONNECTED_TO_SERVER);
SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER);
SET_EXPECT2(INTERNET_STATUS_SENDING_REQUEST, 2);
SET_EXPECT2(INTERNET_STATUS_REQUEST_SENT, 2);
......@@ -836,7 +816,7 @@ static void InternetReadFileExA_test(int flags)
CHECK_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME);
CHECK_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED);
}
else todo_wine
else
{
CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME);
CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED);
......@@ -975,8 +955,6 @@ static void InternetReadFileExA_test(int flags)
abort:
SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0));
if (hor) {
SET_WINE_ALLOW(INTERNET_STATUS_CLOSING_CONNECTION);
SET_WINE_ALLOW(INTERNET_STATUS_CONNECTION_CLOSED);
rc = InternetCloseHandle(hor);
ok ((rc != 0), "InternetCloseHandle of handle opened by HttpOpenRequestA failed\n");
rc = InternetCloseHandle(hor);
......@@ -2640,7 +2618,7 @@ static void test_url_caching(int port, int *num_retrievals)
r = HttpSendRequest(hr, NULL, 0, NULL, 0);
ok(r, "HttpSendRequest failed\n");
ok(*num_retrievals == 1, "expected 1 retrievals\n");
ok(*num_retrievals == 1, "expected 1 retrievals, got %d\n", *num_retrievals);
count = 0;
memset(buffer, 0, sizeof buffer);
......@@ -3406,7 +3384,7 @@ START_TEST(http)
pInternetSetStatusCallbackA = (void*)GetProcAddress(hdll, "InternetSetStatusCallbackA");
init_status_tests();
if(0)test_InternetCloseHandle();
test_InternetCloseHandle();
InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[0]);
InternetReadFile_test(INTERNET_FLAG_ASYNC, &test_data[1]);
InternetReadFile_test(0, &test_data[1]);
......
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