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.
914 lines
25 KiB
914 lines
25 KiB
#include <windows.h>
|
|
#include <ole2.h>
|
|
#include <stdio.h>
|
|
#include <urlmon.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <urlmon.hxx>
|
|
|
|
#define IE5
|
|
|
|
#ifdef PRODUCT_PROF
|
|
extern "C" void _stdcall StartCAP(void);
|
|
extern "C" void _stdcall StopCAP(void);
|
|
extern "C" void _stdcall SuspendCAP(void);
|
|
extern "C" void _stdcall ResumeCAP(void);
|
|
extern "C" void _stdcall StartCAPAll(void);
|
|
extern "C" void _stdcall StopCAPAll(void);
|
|
#else
|
|
#define StartCAP()
|
|
#define StopCAP()
|
|
#define SuspendCAP()
|
|
#define ResumeCAP()
|
|
#define StartCAPAll()
|
|
#define StopCAPAll()
|
|
#endif
|
|
|
|
#define FLAG_TRACE 1
|
|
#define FLAG_DUMPDATA 2
|
|
#define MAX_DOWNLOADS 2000
|
|
#define MAX_URL INTERNET_MAX_URL_LENGTH
|
|
const INT BUF_SIZE = 2 * 1024;
|
|
const INT MAX_BUF_SIZE = 1024 * 16;
|
|
|
|
BOOL g_dwVerbose = 0;
|
|
DWORD g_dwNumDownloads = 1;
|
|
DWORD g_dwDownloads = 1;
|
|
DWORD g_dwTotalBytes = 0;
|
|
DWORD g_dwCacheFlag = BINDF_NOWRITECACHE | BINDF_GETNEWESTVERSION;
|
|
DWORD dwBuf_Size = BUF_SIZE;
|
|
LPWSTR g_pwzUrl = NULL;
|
|
LPCSTR g_pInfile = NULL;
|
|
LPCSTR g_pTitle = NULL;
|
|
LPCSTR g_pRun = NULL;
|
|
LPCSTR g_pModule = NULL;
|
|
HANDLE g_hCompleted;
|
|
|
|
__int64 g_ibeg = 0, g_iend, g_ifreq;
|
|
|
|
//------------------------------------------------------------------------
|
|
// Class: COInetProtocolHook
|
|
//
|
|
// Purpose: Sample Implementation of ProtocolSink and BindInfo
|
|
// interface for simplified urlmon async download
|
|
//
|
|
// Interfaces:
|
|
// [Needed For All]
|
|
// IOInetProtocolSink
|
|
// - provide sink for pluggable prot's callback
|
|
// IOInetBindInfo
|
|
// - provide the bind options
|
|
//
|
|
// [Needed For Http]
|
|
// IServiceProvider
|
|
// - used to query http specific services
|
|
// e.g. HttpNegotiate, Authentication, UIWindow
|
|
// IHttpNegotiate
|
|
// - http negotiation service, it has two methods,
|
|
// one is the http pluggable protocol asks the
|
|
// client for additional headers, the other is
|
|
// callback for returned http server status
|
|
// e.g 200, 401, etc.
|
|
//
|
|
//
|
|
// Author: DanpoZ (Danpo Zhang)
|
|
//
|
|
// History: 11-20-97 Created
|
|
// 05-19-98 Modified to act as performance test
|
|
//
|
|
// NOTE: IOInetXXXX == IInternetXXXX
|
|
// on the SDK, you will see IInternetXXXX, these are same
|
|
// interfaces
|
|
//
|
|
//------------------------------------------------------------------------
|
|
class COInetProtocolHook : public IOInetProtocolSink,
|
|
public IOInetBindInfo,
|
|
public IHttpNegotiate,
|
|
public IServiceProvider
|
|
{
|
|
public:
|
|
COInetProtocolHook(HANDLE g_hCompleted, IOInetProtocol* pProt);
|
|
|
|
virtual ~COInetProtocolHook();
|
|
|
|
// IUnknown methods
|
|
STDMETHODIMP QueryInterface(REFIID iid, void **ppvObj);
|
|
STDMETHODIMP_(ULONG) AddRef(void);
|
|
STDMETHODIMP_(ULONG) Release(void);
|
|
|
|
// IOInetProtocolSink methods
|
|
STDMETHODIMP Switch( PROTOCOLDATA *pStateInfo);
|
|
STDMETHODIMP ReportProgress( ULONG ulStatusCode, LPCWSTR szStatusText);
|
|
STDMETHODIMP ReportData(
|
|
DWORD grfBSCF,
|
|
ULONG ulProgress,
|
|
ULONG ulProgressMax
|
|
);
|
|
STDMETHODIMP ReportResult(
|
|
HRESULT hrResult,
|
|
DWORD dwError,
|
|
LPCWSTR wzResult
|
|
);
|
|
|
|
//IOInetBindInfo methods
|
|
STDMETHODIMP GetBindInfo(
|
|
DWORD *grfBINDF,
|
|
BINDINFO * pbindinfo
|
|
);
|
|
STDMETHODIMP GetBindString(
|
|
ULONG ulStringType,
|
|
LPOLESTR *ppwzStr,
|
|
ULONG cEl,
|
|
ULONG *pcElFetched
|
|
);
|
|
|
|
//IService Provider methods
|
|
STDMETHODIMP QueryService(
|
|
REFGUID guidService,
|
|
REFIID riid,
|
|
void **ppvObj
|
|
);
|
|
|
|
//IHttpNegotiate methods
|
|
STDMETHODIMP BeginningTransaction(
|
|
LPCWSTR szURL,
|
|
LPCWSTR szHeaders,
|
|
DWORD dwReserved,
|
|
LPWSTR *pszAdditionalHeaders
|
|
);
|
|
|
|
STDMETHODIMP OnResponse(
|
|
DWORD dwResponseCode,
|
|
LPCWSTR szResponseHeaders,
|
|
LPCWSTR szRequestHeaders,
|
|
LPWSTR *pszAdditionalHeaders
|
|
);
|
|
private:
|
|
IOInetProtocol* _pProt;
|
|
HANDLE _hCompleted;
|
|
CRefCount _CRefs;
|
|
};
|
|
|
|
|
|
typedef struct tagInfo
|
|
{
|
|
WCHAR wzUrl[INTERNET_MAX_URL_LENGTH];
|
|
IOInetProtocol* pProt;
|
|
COInetProtocolHook* pHook;
|
|
IOInetProtocolSink* pSink;
|
|
IOInetBindInfo* pBindInfo;
|
|
} INFO, *PINFO;
|
|
|
|
typedef BOOL (WINAPI *PFNSPA)(HANDLE, DWORD);
|
|
|
|
INFO Info[MAX_DOWNLOADS];
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
|
|
void MylstrcpyW(WCHAR *pwd, WCHAR *pws)
|
|
{
|
|
while (*pws)
|
|
{
|
|
*pwd++ = *pws++;
|
|
}
|
|
*pwd = 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
WCHAR *MyDupA2W(LPSTR pa)
|
|
{
|
|
int i;
|
|
WCHAR *pw, *pwd;
|
|
|
|
i = lstrlen(pa);
|
|
pw = (WCHAR *)CoTaskMemAlloc((i+1) * sizeof(WCHAR));
|
|
pwd = pw;
|
|
while (*pa)
|
|
{
|
|
*pwd++ = (WCHAR)*pa++;
|
|
}
|
|
*pwd++ = 0;
|
|
|
|
return pw;
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
void SetSingleProcessorAffinity()
|
|
{
|
|
PFNSPA pfn;
|
|
|
|
pfn = (PFNSPA)GetProcAddress(GetModuleHandleA("KERNEL32.DLL"),
|
|
"SetProcessAffinityMask");
|
|
|
|
if (pfn)
|
|
{
|
|
pfn(GetCurrentProcess(), 1);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
VOID DisplayUsage(char **argv)
|
|
{
|
|
printf("Usage: %s /u:url [/n:# /t:Title /r:RunStr /v:#]\n", argv[0]);
|
|
printf(" %s /i:infile [/t:Title /r:RunStr /v:#]\n", argv[0]);
|
|
printf(" /c - write to cache (default is NOWRITECACHE)\n");
|
|
printf(" /g - read from cache (default is GETNEWESTVERSION)\n");
|
|
printf(" /d - direct read (default uses QueryDataAvailable)\n");
|
|
printf(" /l:# - read buffer length\n");
|
|
printf(" /m:module - pre load module\n");
|
|
printf(" /n:# - download # times.\n");
|
|
printf(" /1 - single processor affinity (default multiprocessor)\n");
|
|
printf(" /v:# - verbose level 1=trace 2=dump data\n");
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
BOOL ProcessCommandLine(int argcIn, char **argvIn)
|
|
{
|
|
BOOL bRC = FALSE;
|
|
int argc = argcIn;
|
|
char **argv = argvIn;
|
|
|
|
argv++; argc--;
|
|
if(argc == 0)
|
|
{
|
|
DisplayUsage(argvIn);
|
|
return(FALSE);
|
|
}
|
|
|
|
while( argc > 0 && argv[0][0] == '/' )
|
|
{
|
|
switch (argv[0][1])
|
|
{
|
|
case 'c':
|
|
case 'C':
|
|
g_dwCacheFlag &= ~BINDF_NOWRITECACHE;
|
|
break;
|
|
case 'g':
|
|
case 'G':
|
|
g_dwCacheFlag &= ~BINDF_GETNEWESTVERSION;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
g_dwCacheFlag |= BINDF_DIRECT_READ;
|
|
break;
|
|
case 'i':
|
|
case 'I':
|
|
g_pInfile = &argv[0][3];
|
|
bRC = TRUE;
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
dwBuf_Size = atoi(&argv[0][3]);
|
|
if(dwBuf_Size > MAX_BUF_SIZE)
|
|
dwBuf_Size = MAX_BUF_SIZE;
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
g_pModule = &argv[0][3];
|
|
break;
|
|
case 'n':
|
|
case 'N':
|
|
g_dwNumDownloads = (DWORD)atoi(&argv[0][3]);
|
|
g_dwNumDownloads = max(1, g_dwNumDownloads);
|
|
g_dwNumDownloads = min(MAX_DOWNLOADS, g_dwNumDownloads);
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
g_pRun = &argv[0][3];
|
|
break;
|
|
case 't':
|
|
case 'T':
|
|
g_pTitle = &argv[0][3];
|
|
break;
|
|
case 'u':
|
|
case 'U':
|
|
g_pwzUrl = MyDupA2W(&argv[0][3]);
|
|
bRC = TRUE;
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
g_dwVerbose = (DWORD)atoi(&argv[0][3]);
|
|
break;
|
|
case '1':
|
|
SetSingleProcessorAffinity();
|
|
break;
|
|
case '?':
|
|
default:
|
|
DisplayUsage(argvIn);
|
|
bRC = FALSE;
|
|
}
|
|
argv++; argc--;
|
|
}
|
|
|
|
return(bRC);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
BOOL BuildInfoList(PINFO pInfo, DWORD dwNumDownloads)
|
|
{
|
|
DWORD i = 0;
|
|
|
|
if(g_pInfile != NULL)
|
|
{
|
|
TCHAR szName[INTERNET_MAX_URL_LENGTH+1];
|
|
FILE *fp;
|
|
|
|
if((fp = fopen(g_pInfile, "r")) == NULL)
|
|
{
|
|
printf("error opening file:%s GLE=%d\n", g_pInfile, GetLastError());
|
|
return NULL;
|
|
}
|
|
|
|
while(fgets(szName, INTERNET_MAX_URL_LENGTH, fp) != NULL)
|
|
{
|
|
if(szName[0] != '#')
|
|
{
|
|
szName[strlen(szName) - sizeof(char)] = '\0';
|
|
|
|
int rc;
|
|
rc = MultiByteToWideChar(CP_ACP, 0, szName, -1, (pInfo+i)->wzUrl, INTERNET_MAX_URL_LENGTH);
|
|
if (!rc)
|
|
{
|
|
(pInfo+i)->wzUrl[INTERNET_MAX_URL_LENGTH-1] = 0;
|
|
wprintf(L"BuildInfoList:string too long; truncated to %s\n", (pInfo+i)->wzUrl);
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
g_dwNumDownloads = i;
|
|
fclose(fp);
|
|
}
|
|
else
|
|
{
|
|
for(i =0; i < dwNumDownloads; i++)
|
|
{
|
|
MylstrcpyW((pInfo+i)->wzUrl, g_pwzUrl);
|
|
}
|
|
}
|
|
|
|
g_dwDownloads = g_dwNumDownloads;
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
HRESULT StartDownloads(IOInetSession* pSession, PINFO pInfo, DWORD dwNumDownloads)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
for(DWORD i =0; i < dwNumDownloads; i++)
|
|
{
|
|
// Create a pluggable protocol
|
|
hr = pSession->CreateBinding(
|
|
NULL, // [in ] BindCtx, always NULL
|
|
(pInfo+i)->wzUrl, // [in ] url
|
|
NULL, // [in ] IUnknown for Aggregration
|
|
NULL, // [out] IUNknown for Aggregration
|
|
&(pInfo+i)->pProt, // [out] return pProt pointer
|
|
0 // [in ] bind option, pass 0
|
|
);
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("MAIN: Session->CreateBinding: %lx\n", hr);
|
|
|
|
// Create a protocolHook (sink) and Start the async operation
|
|
if( hr == NOERROR )
|
|
{
|
|
(pInfo+i)->pHook = new COInetProtocolHook(g_hCompleted, (pInfo+i)->pProt);
|
|
(pInfo+i)->pSink = NULL;
|
|
(pInfo+i)->pBindInfo = NULL;
|
|
|
|
if( (pInfo+i)->pHook )
|
|
{
|
|
hr = (pInfo+i)->pHook->QueryInterface(IID_IOInetProtocolSink, (void**)&(pInfo+i)->pSink);
|
|
hr = (pInfo+i)->pHook->QueryInterface(IID_IOInetBindInfo, (void**)&(pInfo+i)->pBindInfo);
|
|
}
|
|
|
|
if( (pInfo+i)->pProt && (pInfo+i)->pSink && (pInfo+i)->pBindInfo )
|
|
{
|
|
hr = (pInfo+i)->pProt->Start(
|
|
(pInfo+i)->wzUrl,
|
|
(pInfo+i)->pSink,
|
|
(pInfo+i)->pBindInfo,
|
|
PI_FORCE_ASYNC,
|
|
0
|
|
);
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("MAIN: pProtocol->Start: %lx\n", hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------
|
|
VOID CleanInfoList(PINFO pInfo, DWORD dwNumDownloads)
|
|
{
|
|
for(DWORD i = 0; i < dwNumDownloads; i++)
|
|
{
|
|
if((pInfo+i)->pProt)
|
|
(pInfo+i)->pProt->Terminate(0);
|
|
|
|
if( (pInfo+i)->pSink )
|
|
{
|
|
(pInfo+i)->pSink->Release();
|
|
}
|
|
if( (pInfo+i)->pBindInfo )
|
|
{
|
|
(pInfo+i)->pBindInfo->Release();
|
|
}
|
|
if( (pInfo+i)->pHook )
|
|
{
|
|
(pInfo+i)->pHook->Release();
|
|
}
|
|
|
|
// release COM objects
|
|
if( (pInfo+i)->pProt )
|
|
{
|
|
//
|
|
// BUG (POSSIBLE RESOURCE LEAK)
|
|
// If the pProt is IE's http/gopher/ftp implementation,
|
|
// calling pProt->Release() now might cause resource leak
|
|
// since pProt (although finished the download), might
|
|
// be still waiting wininet to call back about the
|
|
// confirmation of the handle closing.
|
|
// The correct time to release pProt is to wait after
|
|
// pProtSink get destroyed.
|
|
//
|
|
(pInfo+i)->pProt->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------
|
|
//
|
|
// Purpose: create a session object
|
|
// Get a pluggable procotol from the session
|
|
// Start the pluggable protocol async download
|
|
//
|
|
// Author: DanpoZ (Danpo Zhang)
|
|
//
|
|
// History: 11-20-97 Created
|
|
//
|
|
//------------------------------------------------------------------------
|
|
int _cdecl main(int argc, char** argv)
|
|
{
|
|
IOInetSession* pSession = NULL;
|
|
IOInetProtocol* pProt = NULL;
|
|
HRESULT hr = NOERROR;
|
|
DWORD dwLoadTime;
|
|
HMODULE hMod = NULL;
|
|
|
|
if(!ProcessCommandLine(argc, argv))
|
|
return 0;
|
|
|
|
// Init COM
|
|
CoInitialize(NULL);
|
|
|
|
if(g_pModule != NULL)
|
|
{
|
|
hMod = LoadLibrary(g_pModule);
|
|
}
|
|
|
|
g_hCompleted = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
// Get a Session
|
|
hr = CoInternetGetSession(0, &pSession, 0);
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("MAIN: Created Session: %lx\n", hr);
|
|
|
|
if( hr == NOERROR )
|
|
{
|
|
if(!BuildInfoList(&Info[0], g_dwNumDownloads))
|
|
return(2);
|
|
|
|
hr = StartDownloads(pSession, &Info[0], g_dwNumDownloads);
|
|
|
|
if( hr == NOERROR )
|
|
{
|
|
// wait until the async download finishes
|
|
WaitForSingleObject(g_hCompleted, INFINITE);
|
|
}
|
|
|
|
StopCAP();
|
|
QueryPerformanceCounter((LARGE_INTEGER *)&g_iend);
|
|
QueryPerformanceFrequency((LARGE_INTEGER *)&g_ifreq);
|
|
|
|
dwLoadTime = (LONG)(((g_iend - g_ibeg) * 1000) / g_ifreq);
|
|
float fKB;
|
|
float fSec;
|
|
float fKBSec;
|
|
|
|
if(dwLoadTime == 0)
|
|
dwLoadTime = 1;
|
|
fKB = ((float)g_dwTotalBytes)/1024;
|
|
fSec = ((float)dwLoadTime)/1000;
|
|
fKBSec = fKB / fSec;
|
|
printf("%s,%s,%d,%d,%2.0f\n",
|
|
g_pTitle ?g_pTitle :"Oinetperf",
|
|
g_pRun ?g_pRun :"1",
|
|
dwLoadTime, g_dwTotalBytes, fKBSec);
|
|
|
|
CleanInfoList(&Info[0], g_dwNumDownloads);
|
|
|
|
}
|
|
|
|
|
|
if( pSession )
|
|
{
|
|
pSession->Release();
|
|
}
|
|
|
|
CoTaskMemFree(g_pwzUrl);
|
|
|
|
if((g_pModule != NULL) && (hMod != NULL))
|
|
{
|
|
FreeLibrary(hMod);
|
|
}
|
|
|
|
// kill COM
|
|
CoUninitialize();
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
COInetProtocolHook::COInetProtocolHook
|
|
(
|
|
HANDLE g_hCompleted,
|
|
IOInetProtocol* pProt
|
|
)
|
|
{
|
|
_hCompleted = g_hCompleted;
|
|
_pProt = pProt;
|
|
}
|
|
|
|
COInetProtocolHook::~COInetProtocolHook()
|
|
{
|
|
CloseHandle(_hCompleted);
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::QueryInterface(REFIID iid, void **ppvObj)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
*ppvObj = NULL;
|
|
|
|
if( iid == IID_IUnknown || iid == IID_IOInetProtocolSink )
|
|
{
|
|
*ppvObj = static_cast<IOInetProtocolSink*>(this);
|
|
}
|
|
else
|
|
if( iid == IID_IOInetBindInfo )
|
|
{
|
|
*ppvObj = static_cast<IOInetBindInfo*>(this);
|
|
}
|
|
else
|
|
if( iid == IID_IServiceProvider)
|
|
{
|
|
*ppvObj = static_cast<IServiceProvider*>(this);
|
|
}
|
|
else
|
|
if( iid == IID_IHttpNegotiate )
|
|
{
|
|
*ppvObj = static_cast<IHttpNegotiate*>(this);
|
|
}
|
|
else
|
|
{
|
|
hr = E_NOINTERFACE;
|
|
}
|
|
|
|
if( *ppvObj )
|
|
{
|
|
AddRef();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
ULONG
|
|
COInetProtocolHook::AddRef(void)
|
|
{
|
|
LONG lRet = ++_CRefs;
|
|
return lRet;
|
|
}
|
|
|
|
ULONG
|
|
COInetProtocolHook::Release(void)
|
|
{
|
|
LONG lRet = --_CRefs;
|
|
|
|
if (lRet == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::Switch( PROTOCOLDATA *pStateInfo)
|
|
{
|
|
printf("Are you crazy? I don't know how to do Thread switching!!!\n");
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::ReportProgress( ULONG ulStatusCode, LPCWSTR szStatusText)
|
|
{
|
|
switch( ulStatusCode )
|
|
{
|
|
case BINDSTATUS_FINDINGRESOURCE:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(
|
|
L"CALLBACK(ReportProgress): Resolving name %s\n", szStatusText );
|
|
break;
|
|
|
|
case BINDSTATUS_CONNECTING:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(L"CALLBACK(ReportProgress): Connecting to %s\n", szStatusText );
|
|
break;
|
|
|
|
case BINDSTATUS_SENDINGREQUEST:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(L"CALLBACK(ReportProgress): Sending request\n");
|
|
break;
|
|
|
|
case BINDSTATUS_CACHEFILENAMEAVAILABLE:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(L"CALLBACK(ReportProgress): cache filename available\n");
|
|
break;
|
|
|
|
case BINDSTATUS_MIMETYPEAVAILABLE:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(L"CALLBACK(ReportProgress): mimetype available = %s\n", szStatusText);
|
|
break;
|
|
|
|
case BINDSTATUS_REDIRECTING:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(L"CALLBACK(ReportProgress): Redirecting to %s\n", szStatusText);
|
|
break;
|
|
|
|
default:
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
wprintf(L"CALLBACK(ReportProgress): others...\n");
|
|
break;
|
|
}
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::ReportData(
|
|
DWORD grfBSCF,
|
|
ULONG ulProgress,
|
|
ULONG ulProgressMax
|
|
)
|
|
{
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("CALLBACK(ReportData) %d, %d, %d \n", grfBSCF, ulProgress, ulProgressMax);
|
|
|
|
// Pull data via pProt->Read(), here are the possible returned
|
|
// HRESULT values and how we should act upon:
|
|
//
|
|
// if E_PENDING is returned:
|
|
// client already get all the data in buffer, there is nothing
|
|
// can be done here, client should walk away and wait for the
|
|
// next chuck of data, which will be notified via ReportData()
|
|
// callback.
|
|
//
|
|
// if S_FALSE is returned:
|
|
// this is EOF, everything is done, however, client must wait
|
|
// for ReportResult() callback to indicate that the pluggable
|
|
// protocol is ready to shutdown.
|
|
//
|
|
// if S_OK is returned:
|
|
// keep on reading, until you hit E_PENDING/S_FALSE/ERROR, the deal
|
|
// is that the client is supposed to pull ALL the available
|
|
// data in the buffer
|
|
//
|
|
// if none of the above is returning:
|
|
// Error occured, client should decide how to handle it, most
|
|
// commonly, client will call pProt->Abort() to abort the download
|
|
|
|
char *pBuf = (char *)_alloca(dwBuf_Size);
|
|
HRESULT hr = NOERROR;
|
|
ULONG cbRead;
|
|
|
|
while( hr == S_OK )
|
|
{
|
|
cbRead = 0;
|
|
|
|
if (g_ibeg == 0)
|
|
{
|
|
QueryPerformanceCounter((LARGE_INTEGER *)&g_ibeg);
|
|
StartCAP();
|
|
}
|
|
|
|
|
|
// pull data
|
|
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
{
|
|
printf("MAIN: pProtocol->Read attempting %d bytes\n", dwBuf_Size);
|
|
}
|
|
|
|
hr = _pProt->Read((void*)pBuf, dwBuf_Size, &cbRead);
|
|
if( (hr == S_OK || hr == E_PENDING || hr == S_FALSE) && cbRead )
|
|
{
|
|
if( g_dwVerbose & FLAG_DUMPDATA )
|
|
{
|
|
for( ULONG i = 0; i < cbRead; i++)
|
|
{
|
|
printf("%c", pBuf[i]);
|
|
}
|
|
}
|
|
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
{
|
|
printf("MAIN: pProtocol->Read %d bytes\n", cbRead);
|
|
}
|
|
g_dwTotalBytes += cbRead;
|
|
}
|
|
}
|
|
|
|
if( hr == S_FALSE )
|
|
{
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("MAIN: pProtocol->Read returned EOF \n");
|
|
}
|
|
else
|
|
if( hr != E_PENDING )
|
|
{
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
{
|
|
printf("MAIN: pProtocol->Read returned Error %1x \n, hr");
|
|
printf("MAIN: pProtocol->Abort called \n", hr);
|
|
}
|
|
_pProt->Abort(hr, 0);
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::ReportResult(
|
|
HRESULT hrResult,
|
|
DWORD dwError,
|
|
LPCWSTR wzResult
|
|
)
|
|
{
|
|
// This is the last call back from the pluggable protocol,
|
|
// this call is equivlant to the IBindStatusCallBack::OnStopBinding()
|
|
// it basically tells you that the pluggable protocol is ready
|
|
// to shutdown
|
|
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("CALLBACK(ReportResult): Download completed with status %1x\n", hrResult);
|
|
|
|
// set event to the main thread
|
|
if(InterlockedDecrement((LONG*)&g_dwDownloads ) == 0)
|
|
SetEvent(g_hCompleted);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::GetBindInfo(
|
|
DWORD *grfBINDF,
|
|
BINDINFO * pbindinfo
|
|
)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
|
|
// *grfBINDF = BINDF_DIRECT_READ | BINDF_ASYNCHRONOUS | BINDF_PULLDATA;
|
|
// *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA;
|
|
*grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_IGNORESECURITYPROBLEM;
|
|
*grfBINDF |= g_dwCacheFlag;
|
|
|
|
// for HTTP GET, VERB is the only field we interested
|
|
// for HTTP POST, BINDINFO will point to Storage structure which
|
|
// contains data
|
|
BINDINFO bInfo;
|
|
ZeroMemory(&bInfo, sizeof(BINDINFO));
|
|
|
|
// all we need is size and verb field
|
|
bInfo.cbSize = sizeof(BINDINFO);
|
|
bInfo.dwBindVerb = BINDVERB_GET;
|
|
|
|
// src -> dest
|
|
hr = CopyBindInfo(&bInfo, pbindinfo );
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
static LPSTR g_szAcceptStrAll = "*/*";
|
|
HRESULT
|
|
COInetProtocolHook::GetBindString(
|
|
ULONG ulStringType,
|
|
LPOLESTR *ppwzStr,
|
|
ULONG cEl,
|
|
ULONG *pcElFetched
|
|
)
|
|
{
|
|
|
|
HRESULT hr = INET_E_USE_DEFAULT_SETTING;
|
|
|
|
switch (ulStringType)
|
|
{
|
|
case BINDSTRING_HEADERS :
|
|
case BINDSTRING_EXTRA_URL :
|
|
case BINDSTRING_LANGUAGE :
|
|
case BINDSTRING_USERNAME :
|
|
case BINDSTRING_PASSWORD :
|
|
case BINDSTRING_ACCEPT_ENCODINGS:
|
|
case BINDSTRING_URL:
|
|
case BINDSTRING_USER_AGENT :
|
|
case BINDSTRING_POST_COOKIE :
|
|
case BINDSTRING_POST_DATA_MIME:
|
|
break;
|
|
|
|
case BINDSTRING_ACCEPT_MIMES:
|
|
// IE4's http pluggable protocol implementation does not
|
|
// honer INET_E_USE_DEFAULT_SETTING returned by this function
|
|
// starting from IE5, client can just return the USE_DEFAULT
|
|
|
|
// use for ie5 so we don't need a seperate bin for ie4 // #ifndef IE5
|
|
// this will be freed by the caller
|
|
*(ppwzStr + 0) = MyDupA2W(g_szAcceptStrAll);
|
|
*(ppwzStr + 1) = NULL;
|
|
*pcElFetched = 1;
|
|
|
|
hr = NOERROR;
|
|
//#endif
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
COInetProtocolHook::QueryService(
|
|
REFGUID guidService,
|
|
REFIID riid,
|
|
void **ppvObj
|
|
)
|
|
{
|
|
HRESULT hr = E_NOINTERFACE;
|
|
*ppvObj = NULL;
|
|
if( guidService == IID_IHttpNegotiate )
|
|
{
|
|
*ppvObj = static_cast<IHttpNegotiate*>(this);
|
|
}
|
|
|
|
if( *ppvObj )
|
|
{
|
|
AddRef();
|
|
hr = NOERROR;
|
|
}
|
|
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
COInetProtocolHook::BeginningTransaction(
|
|
LPCWSTR szURL,
|
|
LPCWSTR szHeaders,
|
|
DWORD dwReserved,
|
|
LPWSTR *pszAdditionalHeaders
|
|
)
|
|
{
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("HTTPNEGOTIATE: Additional Headers? - No \n");
|
|
*pszAdditionalHeaders = NULL;
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT
|
|
COInetProtocolHook::OnResponse(
|
|
DWORD dwResponseCode,
|
|
LPCWSTR szResponseHeaders,
|
|
LPCWSTR szRequestHeaders,
|
|
LPWSTR *pszAdditionalHeaders
|
|
)
|
|
{
|
|
if(g_dwVerbose & FLAG_TRACE)
|
|
printf("HTTPNEGOTIATE: Http server response code %d\n", dwResponseCode);
|
|
return NOERROR;
|
|
}
|