You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1014 lines
23 KiB
1014 lines
23 KiB
//////////////////////////////////////////////////////////////////////
|
|
// File: stressTest.cpp
|
|
//
|
|
// Copyright (c) 2001 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// Purpose:
|
|
// <Description>
|
|
//
|
|
// History:
|
|
// 05/24/2001 pmidge Created
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "crawler.h"
|
|
|
|
|
|
LPSTR g_szStressTestName = "AsyncWebCrawler";
|
|
LPWSTR g_szDictPath = L"http://mildew/stress/xmldict/5000.xml";
|
|
HINTERNET g_hSession = NULL;
|
|
PXMLDICT g_pDictionary = NULL;
|
|
HANDLE g_hIOCP = NULL;
|
|
HANDLE g_evtMoreUrls = NULL;
|
|
HANDLE g_evtQuit = NULL;
|
|
HANDLE g_arThreads[WORKER_THREADS];
|
|
|
|
LONG g_lRefCount = 0L;
|
|
LONG g_lUrlObjsAlloc = 0L;
|
|
LONG g_lUrlObjsFreed = 0L;
|
|
|
|
DWORD WINAPI WorkerThread(LPVOID pv);
|
|
|
|
|
|
#define CALLBACK_FLAGS ( WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_REDIRECT \
|
|
| WINHTTP_CALLBACK_STATUS_REQUEST_ERROR \
|
|
| WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE \
|
|
| WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE \
|
|
| WINHTTP_CALLBACK_STATUS_READ_COMPLETE \
|
|
| WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING \
|
|
| WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED )
|
|
|
|
|
|
|
|
void
|
|
AddRef(void)
|
|
{
|
|
InterlockedIncrement(&g_lRefCount);
|
|
}
|
|
|
|
|
|
void
|
|
Release(void)
|
|
{
|
|
InterlockedDecrement(&g_lRefCount);
|
|
|
|
if( g_lRefCount == 0 )
|
|
SetEvent(g_evtQuit);
|
|
}
|
|
|
|
|
|
class Url
|
|
{
|
|
public:
|
|
Url(LPSTR host, LPSTR object, USHORT port)
|
|
{
|
|
this->host = __ansitowide(host);
|
|
this->object = __ansitowide(object);
|
|
this->port = port;
|
|
connect = NULL;
|
|
request = NULL;
|
|
bytes = 0L;
|
|
read = 0L;
|
|
closed = FALSE;
|
|
buffer = NULL;
|
|
qda = FALSE;
|
|
pending = FALSE;
|
|
|
|
InterlockedIncrement(&g_lUrlObjsAlloc);
|
|
}
|
|
|
|
~Url()
|
|
{
|
|
if( !HandlesClosed() )
|
|
{
|
|
if( connect )
|
|
{
|
|
WinHttpSetStatusCallback(
|
|
connect,
|
|
NULL,
|
|
CALLBACK_FLAGS,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if( request )
|
|
{
|
|
WinHttpSetStatusCallback(
|
|
request,
|
|
NULL,
|
|
CALLBACK_FLAGS,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
CloseHandles();
|
|
}
|
|
|
|
if( connect )
|
|
Release();
|
|
|
|
if( request )
|
|
Release();
|
|
|
|
delete [] host;
|
|
delete [] object;
|
|
|
|
if( buffer )
|
|
delete [] buffer;
|
|
|
|
InterlockedIncrement(&g_lUrlObjsFreed);
|
|
}
|
|
|
|
public:
|
|
LPWSTR Host(void) { return host; }
|
|
LPWSTR Object(void) { return object; }
|
|
USHORT Port(void) { return port; }
|
|
void Connect(HINTERNET hConnect) { AddRef(); connect = hConnect; }
|
|
void Request(HINTERNET hRequest) { AddRef(); request = hRequest; }
|
|
HINTERNET Connect(void) { return connect; }
|
|
HINTERNET Request(void) { return request; }
|
|
void Read(DWORD cbData);
|
|
void CloseHandles(void) { closed=TRUE; WinHttpCloseHandle(request); WinHttpCloseHandle(connect); }
|
|
BOOL HandlesClosed(void) { return closed; }
|
|
BOOL IsConnect(HINTERNET hInternet) { return (hInternet == connect); }
|
|
BOOL IsPending(void) { return pending; }
|
|
|
|
private:
|
|
LPWSTR host;
|
|
LPWSTR object;
|
|
USHORT port;
|
|
HINTERNET connect;
|
|
HINTERNET request;
|
|
LPBYTE buffer;
|
|
DWORD bytes;
|
|
DWORD read;
|
|
BOOL qda;
|
|
BOOL pending;
|
|
BOOL closed;
|
|
};
|
|
|
|
|
|
typedef class Url URL;
|
|
typedef class Url* PURL;
|
|
|
|
|
|
BOOL Initialize(void);
|
|
void Cleanup(void);
|
|
BOOL NavigateAsync(PURL pUrl);
|
|
void DumpHeaders(PURL pUrl);
|
|
DWORD GetContentLength(PURL pUrl);
|
|
|
|
|
|
// main function
|
|
BOOL
|
|
WinHttp_StressTest()
|
|
{
|
|
BOOL bContinueStress = TRUE;
|
|
BOOL bContinue = TRUE;
|
|
BSTR bsWord = NULL;
|
|
PURL pUrl = NULL;
|
|
CHAR url[MAX_PATH];
|
|
|
|
if( !Initialize() )
|
|
{
|
|
LogText("[tid=%#0.8x] failed to initialize, exiting", GetCurrentThreadId());
|
|
bContinueStress = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
while( bContinue && !IsTimeToExitStress() )
|
|
{
|
|
LogText("[tid=%#0.8x] processing urls...", GetCurrentThreadId());
|
|
|
|
for(int n=0; n < 100; n++)
|
|
{
|
|
if( bsWord = g_pDictionary->GetWord() )
|
|
{
|
|
wsprintf(url, "www.%S.com", bsWord);
|
|
|
|
if( pUrl = new URL(url, "/", 80) )
|
|
PostQueuedCompletionStatus(g_hIOCP, 0L, (ULONG_PTR) pUrl, NULL);
|
|
|
|
/*
|
|
if( pUrl = new URL(url, "/", 443) )
|
|
PostQueuedCompletionStatus(g_hIOCP, 0L, (ULONG_PTR) pUrl, NULL);
|
|
*/
|
|
|
|
SysFreeString(bsWord);
|
|
}
|
|
else
|
|
{
|
|
LogText("[tid=%#0.8x] urls exhausted, signaling workers to exit", GetCurrentThreadId());
|
|
//bContinueStress = FALSE; // DEBUGONLY
|
|
bContinue = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
WaitForSingleObject(g_evtMoreUrls, 120000);
|
|
LogText("[tid=%#0.8x] url object stats: alloc=%d; freed=%d", GetCurrentThreadId(), g_lUrlObjsAlloc, g_lUrlObjsFreed);
|
|
}
|
|
|
|
//
|
|
// post quit messages and wait
|
|
//
|
|
|
|
LogText("[tid=%#0.8x] waiting for threads to exit...", GetCurrentThreadId());
|
|
|
|
for(int n=0; n < WORKER_THREADS; n++)
|
|
{
|
|
PostQueuedCompletionStatus(g_hIOCP, 0L, CK_QUIT_THREAD, NULL);
|
|
}
|
|
|
|
WaitForMultipleObjects(WORKER_THREADS, g_arThreads, TRUE, INFINITE);
|
|
|
|
for(int n=0; n < WORKER_THREADS; n++)
|
|
{
|
|
CloseHandle(g_arThreads[n]);
|
|
g_arThreads[n] = NULL;
|
|
}
|
|
|
|
while( g_lRefCount > 0 )
|
|
{
|
|
LogText("[tid=%#0.8x] waiting for %d internet handles...", GetCurrentThreadId(), g_lRefCount);
|
|
WaitForSingleObject(g_evtQuit, 5000);
|
|
}
|
|
|
|
exit:
|
|
|
|
Cleanup();
|
|
|
|
LogText("[tid=%#0.8x] final url object stats: alloc=%d; freed=%d", GetCurrentThreadId(), g_lUrlObjsAlloc, g_lUrlObjsFreed);
|
|
|
|
return bContinueStress;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
WorkerThread(LPVOID pv)
|
|
{
|
|
DWORD bytes = 0L;
|
|
ULONG_PTR key = 0L;
|
|
LPOVERLAPPED lpo = NULL;
|
|
PURL pUrl = NULL;
|
|
BOOL bQuit = FALSE;
|
|
|
|
while( !bQuit )
|
|
{
|
|
if( !GetQueuedCompletionStatus(g_hIOCP, &bytes, &key, &lpo, 7000) )
|
|
{
|
|
if( GetLastError() == WAIT_TIMEOUT )
|
|
{
|
|
SetEvent(g_evtMoreUrls);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch( key )
|
|
{
|
|
case CK_QUIT_THREAD :
|
|
{
|
|
bQuit = TRUE;
|
|
}
|
|
break;
|
|
|
|
case NULL :
|
|
{
|
|
LogText("[tid=%#0.8x] ERROR! NULL pUrl dequeued!", GetCurrentThreadId());
|
|
}
|
|
break;
|
|
|
|
default :
|
|
{
|
|
pUrl = (PURL) key;
|
|
NavigateAsync(pUrl);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LogText("[tid=%#0.8x] exiting", GetCurrentThreadId());
|
|
return 1L;
|
|
}
|
|
|
|
|
|
BOOL
|
|
NavigateAsync(PURL pUrl)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
HINTERNET hConnect = NULL;
|
|
HINTERNET hRequest = NULL;
|
|
LPCWSTR arAcceptTypes[] = {L"*/*",L"image/*",L"text/*",NULL};
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// open connect handle
|
|
//-------------------------------------------------------------------------------------
|
|
hConnect = WinHttpConnect(
|
|
g_hSession,
|
|
pUrl->Host(),
|
|
pUrl->Port(),
|
|
0L
|
|
);
|
|
|
|
if( hConnect )
|
|
{
|
|
pUrl->Connect(hConnect);
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
LogText(
|
|
"[tid=%#0.8x] WinHttpConnect failed for servername %S, error %d [%s]",
|
|
GetCurrentThreadId(),
|
|
pUrl->Host(),
|
|
dwError,
|
|
MapErrorToString(dwError)
|
|
);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// set the callback
|
|
//-------------------------------------------------------------------------------------
|
|
WinHttpSetStatusCallback(
|
|
pUrl->Connect(),
|
|
MyStatusCallback,
|
|
CALLBACK_FLAGS,
|
|
NULL
|
|
);
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// open request handle
|
|
//-------------------------------------------------------------------------------------
|
|
hRequest = WinHttpOpenRequest(
|
|
pUrl->Connect(),
|
|
L"GET",
|
|
pUrl->Object(),
|
|
NULL,
|
|
NULL,
|
|
arAcceptTypes,
|
|
((pUrl->Port() == 80) ? 0L : WINHTTP_FLAG_SECURE)
|
|
);
|
|
|
|
if( hRequest )
|
|
{
|
|
pUrl->Request(hRequest);
|
|
}
|
|
else
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
LogText(
|
|
"[tid=%#0.8x] WinHttpOpenRequest failed for %S, error %d [%s]",
|
|
GetCurrentThreadId(),
|
|
pUrl->Object(),
|
|
dwError,
|
|
MapErrorToString(dwError)
|
|
);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// send the request - this is the first opportunity for a call to go async
|
|
//-------------------------------------------------------------------------------------
|
|
if( !WinHttpSendRequest(pUrl->Request(), NULL, 0L, NULL, 0L, 0L, (DWORD_PTR) pUrl) )
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if( dwError == ERROR_IO_PENDING )
|
|
{
|
|
#if 0
|
|
LogText(
|
|
"[tid=%#0.8x; con=%#0.8x; req=%#0.8x] %s://%S%S request went async...",
|
|
GetCurrentThreadId(),
|
|
hConnect,
|
|
hRequest,
|
|
((pUrl->Port() == 80) ? "http" : "https"),
|
|
pUrl->Host(),
|
|
pUrl->Object()
|
|
);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
LogText(
|
|
"[tid=%#0.8x; con=%#0.8x; req=%#0.8x] %s://%S%S request failed: %d [%s]!",
|
|
GetCurrentThreadId(),
|
|
hConnect,
|
|
hRequest,
|
|
((pUrl->Port() == 80) ? "http" : "https"),
|
|
pUrl->Host(),
|
|
pUrl->Object(),
|
|
dwError,
|
|
MapErrorToString(dwError)
|
|
);
|
|
|
|
goto quit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogText("[tid=%#0.8x] ERROR! WinHttpSendRequest returned TRUE in async mode!!!", GetCurrentThreadId());
|
|
goto quit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// if we get here, we've succeeded in our mission, set exit code to true
|
|
//-------------------------------------------------------------------------------------
|
|
bRet = TRUE;
|
|
|
|
|
|
quit:
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// handle errors and exit
|
|
//-------------------------------------------------------------------------------------
|
|
if( !bRet )
|
|
delete pUrl;
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
CALLBACK
|
|
MyStatusCallback(
|
|
HINTERNET hInternet,
|
|
DWORD_PTR dwContext,
|
|
DWORD dwInternetStatus,
|
|
LPVOID lpvStatusInformation,
|
|
DWORD dwStatusInformationLength
|
|
)
|
|
{
|
|
PURL pUrl = (PURL) dwContext;
|
|
|
|
#if 0
|
|
LogText(
|
|
"[tid=%#0.8x; con=%#0.8x; req=%#0.8x] %s://%S%S in %s",
|
|
GetCurrentThreadId(),
|
|
pUrl->Connect(),
|
|
pUrl->Request(),
|
|
((pUrl->Port() == 80) ? "http" : "https"),
|
|
pUrl->Host(),
|
|
pUrl->Object(),
|
|
MapCallbackToString(dwInternetStatus)
|
|
);
|
|
#endif
|
|
|
|
|
|
switch(dwInternetStatus)
|
|
{
|
|
case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE :
|
|
{
|
|
//
|
|
// a WHSR call is completing
|
|
//
|
|
WinHttpReceiveResponse(pUrl->Request(), NULL);
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_REDIRECT :
|
|
{
|
|
pUrl->CloseHandles();
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE :
|
|
{
|
|
//
|
|
// a WHRR call is completing
|
|
//
|
|
pUrl->Read(GetContentLength(pUrl));
|
|
//pUrl->Read(0);
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE :
|
|
{
|
|
//
|
|
// a WHQDA call is completing
|
|
//
|
|
pUrl->Read(dwStatusInformationLength);
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_READ_COMPLETE :
|
|
{
|
|
//
|
|
// a WHRD call is completing
|
|
//
|
|
|
|
#if 0
|
|
DataDump((LPBYTE) lpvStatusInformation, dwStatusInformationLength);
|
|
#endif
|
|
|
|
pUrl->Read(dwStatusInformationLength);
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED :
|
|
{
|
|
if( !pUrl->IsPending() )
|
|
{
|
|
if( !pUrl->HandlesClosed() )
|
|
{
|
|
pUrl->CloseHandles();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING :
|
|
{
|
|
//
|
|
// we're done with this particular URL
|
|
//
|
|
|
|
if( pUrl->IsConnect(hInternet) )
|
|
delete pUrl;
|
|
}
|
|
break;
|
|
|
|
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR :
|
|
{
|
|
#if 0
|
|
WINHTTP_ASYNC_RESULT* pwar = (WINHTTP_ASYNC_RESULT*) lpvStatusInformation;
|
|
|
|
LogText(
|
|
"[tid=%#0.8x; hInternet=%#0.8x] async api error: dwResult=%d; dwError=%s",
|
|
GetCurrentThreadId(),
|
|
hInternet,
|
|
pwar->dwResult,
|
|
MapAsyncErrorToString(pwar->dwError)
|
|
);
|
|
#endif
|
|
pUrl->CloseHandles();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Url::Read(DWORD cbData)
|
|
{
|
|
BOOL bSuccess = FALSE;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
query_data:
|
|
|
|
// if a read is pending, we know that we're handling a READ_COMPLETE callback
|
|
if( !pending )
|
|
{
|
|
// if we haven't recently called WHQDA, do so and handle errors
|
|
if( !qda && !(bytes = cbData) )
|
|
{
|
|
bSuccess = WinHttpQueryDataAvailable(request, &bytes);
|
|
dwError = GetLastError();
|
|
|
|
if( !bSuccess )
|
|
{
|
|
if( dwError != ERROR_IO_PENDING )
|
|
{
|
|
CloseHandles();
|
|
}
|
|
else
|
|
{
|
|
qda = TRUE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// we got here, so there must be some data to read, reset the QDA flag and read data.
|
|
qda = FALSE;
|
|
buffer = new BYTE[bytes];
|
|
bSuccess = WinHttpReadData(request, (LPVOID) buffer, bytes, &read);
|
|
dwError = GetLastError();
|
|
|
|
if( bSuccess && (read == 0) )
|
|
{
|
|
CloseHandles();
|
|
}
|
|
else
|
|
{
|
|
if( dwError == ERROR_IO_PENDING )
|
|
{
|
|
pending = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CloseHandles();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// an async read has completed, did we read anything? if not, close handles and return,
|
|
// otherwise free the old buffer and reset our internal state. then, to keep things
|
|
// rolling, loop back up and call WHQDA.
|
|
if( cbData == 0 )
|
|
{
|
|
pending = FALSE;
|
|
CloseHandles();
|
|
}
|
|
else
|
|
{
|
|
delete [] buffer;
|
|
|
|
buffer = NULL;
|
|
bytes = 0;
|
|
read = 0;
|
|
cbData = 0;
|
|
pending = FALSE;
|
|
|
|
goto query_data;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
GetContentLength(PURL pUrl)
|
|
{
|
|
DWORD dwCL = 0L;
|
|
DWORD cbData = sizeof(DWORD);
|
|
|
|
WinHttpQueryHeaders(
|
|
pUrl->Request(),
|
|
WINHTTP_QUERY_CONTENT_LENGTH + WINHTTP_QUERY_FLAG_NUMBER,
|
|
NULL,
|
|
&dwCL,
|
|
&cbData,
|
|
NULL
|
|
);
|
|
|
|
SetLastError(0);
|
|
|
|
return dwCL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
Initialize(void)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
DWORD dwError = 0L;
|
|
|
|
if( FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)) )
|
|
{
|
|
LogText("failed to initialize COM");
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// open dictionary file
|
|
//-------------------------------------------------------------------------------------
|
|
if( !g_pDictionary )
|
|
{
|
|
g_pDictionary = new XMLDICT(g_szDictPath);
|
|
|
|
if( !g_pDictionary )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
if( g_pDictionary->IsLoaded() )
|
|
{
|
|
LogText("dictionary loaded.");
|
|
}
|
|
else
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// create completion port
|
|
//-------------------------------------------------------------------------------------
|
|
g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0L, MAX_CONCURRENT);
|
|
|
|
if( !g_hIOCP )
|
|
{
|
|
dwError = GetLastError();
|
|
LogText("failed to open completion port, error %d [%s]", dwError, MapErrorToString(dwError));
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// create worker threads
|
|
//-------------------------------------------------------------------------------------
|
|
for(int n=0; n < WORKER_THREADS; n++)
|
|
{
|
|
g_arThreads[n] = CreateThread(
|
|
NULL,
|
|
0L,
|
|
WorkerThread,
|
|
NULL,
|
|
0L,
|
|
NULL
|
|
);
|
|
|
|
if( !g_arThreads[n] )
|
|
{
|
|
dwError = GetLastError();
|
|
LogText("failed to create thread %d, error %d [%s]", n, dwError, MapErrorToString(dwError));
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// create 'no more urls' event
|
|
//-------------------------------------------------------------------------------------
|
|
g_evtMoreUrls = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if( !g_evtMoreUrls )
|
|
{
|
|
dwError = GetLastError();
|
|
LogText("failed to create url event, error %d [%s]", dwError, MapErrorToString(dwError));
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// create 'all requests complete' event
|
|
//-------------------------------------------------------------------------------------
|
|
g_evtQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if( !g_evtQuit )
|
|
{
|
|
dwError = GetLastError();
|
|
LogText("failed to create quit event, error %d [%s]", dwError, MapErrorToString(dwError));
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// open shared session handle
|
|
//-------------------------------------------------------------------------------------
|
|
g_hSession = WinHttpOpen(
|
|
L"foo",
|
|
WINHTTP_ACCESS_TYPE_NAMED_PROXY,
|
|
L"itgproxy",
|
|
L"<local>",
|
|
WINHTTP_FLAG_ASYNC
|
|
);
|
|
|
|
if( !g_hSession )
|
|
{
|
|
dwError = GetLastError();
|
|
LogText("failed to open winhttp, error %d [%s]", dwError, MapErrorToString(dwError));
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// set global timeouts
|
|
//-------------------------------------------------------------------------------------
|
|
bRet = WinHttpSetTimeouts(
|
|
g_hSession,
|
|
60000, // resolve
|
|
10000, // connect
|
|
5000, // send
|
|
5000 // receive
|
|
);
|
|
|
|
if( !bRet )
|
|
{
|
|
dwError = GetLastError();
|
|
LogText("failed to set timeouts, error %d [%s]", dwError, MapErrorToString(dwError));
|
|
}
|
|
|
|
exit:
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
void
|
|
Cleanup(void)
|
|
{
|
|
if( g_pDictionary )
|
|
{
|
|
//g_pDictionary->Reset();
|
|
|
|
delete g_pDictionary;
|
|
g_pDictionary = NULL;
|
|
}
|
|
|
|
if( g_hIOCP != NULL )
|
|
{
|
|
CloseHandle(g_hIOCP);
|
|
g_hIOCP = NULL;
|
|
}
|
|
|
|
if( g_evtMoreUrls != NULL )
|
|
{
|
|
CloseHandle(g_evtMoreUrls);
|
|
g_evtMoreUrls = NULL;
|
|
}
|
|
|
|
if( g_evtQuit != NULL )
|
|
{
|
|
CloseHandle(g_evtQuit);
|
|
g_evtQuit = NULL;
|
|
}
|
|
|
|
if( g_hSession )
|
|
{
|
|
WinHttpCloseHandle(g_hSession);
|
|
g_hSession = NULL;
|
|
}
|
|
|
|
CoUninitialize();
|
|
}
|
|
|
|
|
|
XMLDict::XMLDict(LPWSTR dictname)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BSTR tag = NULL;
|
|
VARIANT_BOOL bSuccess = VARIANT_FALSE;
|
|
VARIANT doc;
|
|
|
|
LogText("loading dictionary...");
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_DOMDocument,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IXMLDOMDocument,
|
|
(void**) &pDoc
|
|
);
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
hr = pDoc->put_async(bSuccess);
|
|
|
|
VariantInit(&doc);
|
|
|
|
V_VT(&doc) = VT_BSTR;
|
|
V_BSTR(&doc) = SysAllocString(dictname);
|
|
|
|
hr = pDoc->load(doc, &bSuccess);
|
|
|
|
if( FAILED(hr) || (bSuccess == VARIANT_FALSE) )
|
|
{
|
|
LogText("failed to load xml dictionary");
|
|
goto quit;
|
|
}
|
|
|
|
hr = pDoc->get_documentElement(&pRoot);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
LogText("couldn\'t find root node!");
|
|
goto quit;
|
|
}
|
|
|
|
tag = SysAllocString(L"keyphrase");
|
|
hr = pDoc->getElementsByTagName(tag, &pList);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
LogText("couldn\'t find any words!");
|
|
goto quit;
|
|
}
|
|
|
|
hr = pList->get_length(&lWords);
|
|
|
|
if( FAILED(hr) )
|
|
{
|
|
LogText("couldn\'t determine the number of words in the list!");
|
|
}
|
|
|
|
szPattern = SysAllocString(L"string");
|
|
lCurrentWord = 0L;
|
|
}
|
|
|
|
quit:
|
|
|
|
VariantClear(&doc);
|
|
|
|
if( tag )
|
|
{
|
|
SysFreeString(tag);
|
|
}
|
|
}
|
|
|
|
|
|
XMLDict::~XMLDict()
|
|
{
|
|
LogText("unloading dictionary...");
|
|
|
|
if( szPattern )
|
|
{
|
|
SysFreeString(szPattern);
|
|
}
|
|
|
|
if( pList )
|
|
{
|
|
pList->Release();
|
|
}
|
|
|
|
if( pRoot )
|
|
{
|
|
pRoot->Release();
|
|
}
|
|
|
|
if( pDoc )
|
|
{
|
|
pDoc->Release();
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
XMLDict::IsLoaded(void)
|
|
{
|
|
LONG state = 0L;
|
|
|
|
if( pDoc )
|
|
{
|
|
pDoc->get_readyState(&state);
|
|
}
|
|
else
|
|
{
|
|
return state;
|
|
}
|
|
|
|
return (state == 4);
|
|
}
|
|
|
|
|
|
BSTR
|
|
XMLDict::GetWord(void)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IXMLDOMNode* pEntry = NULL;
|
|
IXMLDOMNode* pWord = NULL;
|
|
BSTR bsWord = NULL;
|
|
|
|
do_over:
|
|
|
|
hr = pList->get_item(lCurrentWord, &pEntry);
|
|
|
|
if( FAILED(hr) || !pEntry )
|
|
goto quit;
|
|
|
|
++lCurrentWord;
|
|
|
|
hr = pEntry->selectSingleNode(szPattern, &pWord);
|
|
|
|
if( FAILED(hr) || !pWord )
|
|
goto quit;
|
|
|
|
hr = pWord->get_text(&bsWord);
|
|
|
|
if( FAILED(hr) )
|
|
goto quit;
|
|
|
|
// some of the words in the dictionary have apostrophes. urls can't have
|
|
// apostrophes, so we strip them out.
|
|
if( wcschr(bsWord, L'\'') )
|
|
{
|
|
SysFreeString(bsWord);
|
|
bsWord = NULL;
|
|
pEntry->Release();
|
|
pWord->Release();
|
|
goto do_over;
|
|
}
|
|
|
|
quit:
|
|
|
|
if( pEntry )
|
|
pEntry->Release();
|
|
|
|
if( pWord )
|
|
pWord->Release();
|
|
|
|
return bsWord;
|
|
}
|