|
|
/*
* HttpRequest.cpp * * WinHttp.WinHttpRequest COM component * * Copyright (C) 2000 Microsoft Corporation. All rights reserved. * * * Much of this code was stolen from our Xml-Http friends over in * inetcore\xml\http\xmlhttp.cxx. Thanks very much! * */ #include <wininetp.h>
#include "httprequest.hxx"
#include <olectl.h>
/////////////////////////////////////////////////////////////////////////////
// private function prototypes
static void WideCharToUtf8(WCHAR * buffer, UINT cch, BYTE * bytebuffer, UINT * cb); static HRESULT BSTRToUTF8(char ** psz, DWORD * pcbUTF8, BSTR bstr); static HRESULT AsciiToBSTR(BSTR * pbstr, char * sz, int cch); static HRESULT BSTRToAscii(char ** psz, BSTR bstr); static BSTR GetBSTRFromVariant(VARIANT varVariant); static BOOL GetBoolFromVariant(VARIANT varVariant, BOOL fDefault); static DWORD GetDwordFromVariant(VARIANT varVariant, DWORD dwDefault); static long GetLongFromVariant(VARIANT varVariant, long lDefault); static HRESULT CreateVector(VARIANT * pVar, const BYTE * pData, DWORD cElems); static HRESULT ReadFromStream(char ** ppData, ULONG * pcbData, IStream * pStm); static void MessageLoop(); static DWORD UpdateTimeout(DWORD dwTimeout, DWORD dwStartTime); static HRESULT FillExcepInfo(HRESULT hr, EXCEPINFO * pExcepInfo); static BOOL IsValidVariant(VARIANT v);
static BOOL s_fWndClassRegistered; static const char * s_szWinHttpEventMarshallerWndClass = "_WinHttpEventMarshaller";
#define SafeRelease(p) \
{ \ if (p) \ (p)->Release();\ (p) = NULL;\ }
#ifndef HWND_MESSAGE
#define HWND_MESSAGE ((HWND)-3)
#endif
inline BOOL IsValidBstr(BSTR bstr) { // A BSTR can be NULL, or if non-NULL, it should at least
// point to a 2-byte terminating NULL character.
return (bstr == NULL) || (!IsBadReadPtr(bstr, 2)); }
#ifndef WINHTTP_STATIC_LIBRARY
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void ** ppv) { if (rclsid != CLSID_WinHttpRequest) return CLASS_E_CLASSNOTAVAILABLE;
if (riid != IID_IClassFactory || ppv == NULL) return E_INVALIDARG;
CClassFactory * pCF = New CClassFactory();
if (pCF) { *ppv = static_cast<IClassFactory *>(pCF); pCF->AddRef(); return NOERROR; } else { *ppv = NULL; return E_OUTOFMEMORY; } } CClassFactory::CClassFactory() { _cRefs = 0; }
STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void ** ppvObject) { if (ppvObject == NULL) return E_INVALIDARG;
if (riid == IID_IClassFactory || riid == IID_IUnknown) { *ppvObject = static_cast<IClassFactory *>(this); AddRef(); return NOERROR; } else return E_NOINTERFACE; }
ULONG STDMETHODCALLTYPE CClassFactory::AddRef() { return ++_cRefs; }
ULONG STDMETHODCALLTYPE CClassFactory::Release() { if (--_cRefs == 0) { delete this; return 0; }
return _cRefs; }
STDMETHODIMP CClassFactory::CreateInstance(IUnknown * pUnkOuter, REFIID riid, void ** ppvObject) { if (pUnkOuter != NULL) return CLASS_E_NOAGGREGATION;
if (ppvObject == NULL) return E_INVALIDARG;
return CreateHttpRequest(riid, ppvObject); }
STDMETHODIMP CClassFactory::LockServer(BOOL fLock) { return NOERROR; }
#else
STDAPI WinHttpCreateHttpRequestComponent(REFIID riid, void ** ppvObject) { return CreateHttpRequest(riid, ppvObject); }
#endif //WINHTTP_STATIC_LIBRARY
STDMETHODIMP CreateHttpRequest(REFIID riid, void ** ppvObject) { CHttpRequest * pHttpRequest = New CHttpRequest(); HRESULT hr;
if (pHttpRequest) { hr = pHttpRequest->QueryInterface(riid, ppvObject);
if (FAILED(hr)) { delete pHttpRequest; } } else hr = E_OUTOFMEMORY;
return hr; }
/*
* CHttpRequest::CHttpRequest constructor * */
CHttpRequest::CHttpRequest() { Initialize(); }
/*
* CHttpRequest::~CHttpRequest destructor * */
CHttpRequest::~CHttpRequest() { ReleaseResources(); }
HRESULT STDMETHODCALLTYPE CHttpRequest::QueryInterface(REFIID riid, void ** ppv) { HRESULT hr = NOERROR;
if (ppv == NULL) { hr = E_INVALIDARG; } else if (riid == IID_IWinHttpRequest || riid == IID_IDispatch || riid == IID_IUnknown) { *ppv = static_cast<IWinHttpRequest *>(this); AddRef(); } else if (riid == IID_IConnectionPointContainer) { *ppv = static_cast<IConnectionPointContainer *>(this); AddRef(); } else if (riid == IID_IProvideClassInfo) { *ppv = static_cast<IProvideClassInfo *>(static_cast<IProvideClassInfo2 *>(this)); AddRef(); } else if (riid == IID_IProvideClassInfo2) { *ppv = static_cast<IProvideClassInfo2 *>(this); AddRef(); } else hr = E_NOINTERFACE;
return hr; }
ULONG STDMETHODCALLTYPE CHttpRequest::AddRef() { if (GetCurrentThreadId() == _dwMainThreadId) ++_cRefsOnMainThread;
return InterlockedIncrement(&_cRefs); }
ULONG STDMETHODCALLTYPE CHttpRequest::Release() { if (GetCurrentThreadId() == _dwMainThreadId) { if ((--_cRefsOnMainThread == 0) && _fAsync) { // Clean up the Event Marshaller. This must be done
// on the main thread.
_CP.ShutdownEventSinksMarshaller();
// If the worker thread is still running, abort it
// and wait for it to run down.
Abort(); } }
DWORD cRefs = InterlockedDecrement(&_cRefs);
if (cRefs == 0) { delete this; return 0; } else return cRefs; }
HRESULT CHttpRequest::GetHttpRequestTypeInfo(REFGUID guid, ITypeInfo ** ppTypeInfo) { HRESULT hr = NOERROR;
ITypeLib * pTypeLib; char szPath[MAX_PATH]; OLECHAR wszPath[MAX_PATH];
GetModuleFileName(GlobalDllHandle, szPath, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
hr = LoadTypeLib(wszPath, &pTypeLib);
if (SUCCEEDED(hr)) { hr = pTypeLib->GetTypeInfoOfGuid(guid, ppTypeInfo);
pTypeLib->Release(); }
return hr; }
STDMETHODIMP CHttpRequest::GetTypeInfoCount(UINT * pctinfo) { if (!pctinfo) return E_INVALIDARG;
*pctinfo = 1;
return NOERROR; }
STDMETHODIMP CHttpRequest::GetTypeInfo(UINT iTInfo, LCID, ITypeInfo ** ppTInfo) { if (!ppTInfo) return E_INVALIDARG;
*ppTInfo = NULL;
if (iTInfo != 0) return DISP_E_BADINDEX;
if (!_pTypeInfo) { HRESULT hr = GetHttpRequestTypeInfo(IID_IWinHttpRequest, &_pTypeInfo);
if (FAILED(hr)) return hr; }
*ppTInfo = _pTypeInfo; _pTypeInfo->AddRef();
return NOERROR; }
struct IDMAPPING { const OLECHAR * wszMemberName; DISPID dispId; };
static const IDMAPPING IdMapping[] = { { L"Open", DISPID_HTTPREQUEST_OPEN }, { L"SetRequestHeader", DISPID_HTTPREQUEST_SETREQUESTHEADER }, { L"Send", DISPID_HTTPREQUEST_SEND }, { L"Status", DISPID_HTTPREQUEST_STATUS }, { L"WaitForResponse", DISPID_HTTPREQUEST_WAITFORRESPONSE }, { L"GetResponseHeader", DISPID_HTTPREQUEST_GETRESPONSEHEADER }, { L"ResponseBody", DISPID_HTTPREQUEST_RESPONSEBODY }, { L"ResponseText", DISPID_HTTPREQUEST_RESPONSETEXT }, { L"ResponseStream", DISPID_HTTPREQUEST_RESPONSESTREAM }, { L"StatusText", DISPID_HTTPREQUEST_STATUSTEXT }, { L"SetCredentials", DISPID_HTTPREQUEST_SETCREDENTIALS }, { L"SetProxy", DISPID_HTTPREQUEST_SETPROXY }, { L"GetAllResponseHeaders", DISPID_HTTPREQUEST_GETALLRESPONSEHEADERS }, { L"Abort", DISPID_HTTPREQUEST_ABORT }, { L"SetTimeouts", DISPID_HTTPREQUEST_SETTIMEOUTS }, { L"Option", DISPID_HTTPREQUEST_OPTION } };
STDMETHODIMP CHttpRequest::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID , DISPID * rgDispId) { if (riid != IID_NULL) return E_INVALIDARG;
HRESULT hr = NOERROR;
if (cNames > 0) { hr = DISP_E_UNKNOWNNAME;
for (int i = 0; i < (sizeof(IdMapping)/sizeof(IdMapping[0])); i++) { if (StrCmpIW(rgszNames[0], IdMapping[i].wszMemberName) == 0) { hr = NOERROR; rgDispId[0] = IdMapping[i].dispId; break; } } }
return hr; }
// _DispGetOptionalParam
//
// Helper routine to fetch optional parameters. If DispGetParam returns
// DISP_E_PARAMNOTFOUND, the error is converted to NOERROR.
//
static inline HRESULT _DispGetOptionalParam ( DISPPARAMS * pDispParams, DISPID dispid, VARTYPE vt, VARIANT * pvarResult, unsigned int * puArgErr ) { HRESULT hr = DispGetParam(pDispParams, dispid, vt, pvarResult, puArgErr);
return (hr == DISP_E_PARAMNOTFOUND) ? NOERROR : hr; }
STDMETHODIMP CHttpRequest::Invoke(DISPID dispIdMember, REFIID riid, LCID, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr) { HRESULT hr = NOERROR; unsigned int uArgErr;
if (wFlags & ~(DISPATCH_METHOD | DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT)) return E_INVALIDARG;
if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE;
if (IsBadReadPtr(pDispParams, sizeof(DISPPARAMS))) return E_INVALIDARG;
if (!puArgErr) { puArgErr = &uArgErr; } else if (IsBadWritePtr(puArgErr, sizeof(UINT))) { return E_INVALIDARG; }
if (pVarResult) { if (IsBadWritePtr(pVarResult, sizeof(VARIANT))) return E_INVALIDARG;
VariantInit(pVarResult); }
switch (dispIdMember) { case DISPID_HTTPREQUEST_ABORT: { hr = Abort();
break; }
case DISPID_HTTPREQUEST_SETPROXY: { VARIANT varProxySetting; VARIANT varProxyServer; VARIANT varBypassList;
VariantInit(&varProxySetting); VariantInit(&varProxyServer); VariantInit(&varBypassList);
hr = DispGetParam(pDispParams, 0, VT_UI4, &varProxySetting, puArgErr);
if (SUCCEEDED(hr)) { hr = _DispGetOptionalParam(pDispParams, 1, VT_BSTR, &varProxyServer, puArgErr); }
if (SUCCEEDED(hr)) { hr = _DispGetOptionalParam(pDispParams, 2, VT_BSTR, &varBypassList, puArgErr); }
if (SUCCEEDED(hr)) { hr = SetProxy(V_UI4(&varProxySetting), varProxyServer, varBypassList); }
VariantClear(&varProxySetting); VariantClear(&varProxyServer); VariantClear(&varBypassList);
break; }
case DISPID_HTTPREQUEST_SETCREDENTIALS: { VARIANT varUserName; VARIANT varPassword; VARIANT varAuthTarget;
VariantInit(&varUserName); VariantInit(&varPassword); VariantInit(&varAuthTarget);
hr = DispGetParam(pDispParams, 0, VT_BSTR, &varUserName, puArgErr);
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 1, VT_BSTR, &varPassword, puArgErr); }
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 2, VT_UI4, &varAuthTarget, puArgErr); }
if (SUCCEEDED(hr)) { hr = SetCredentials(V_BSTR(&varUserName), V_BSTR(&varPassword), V_UI4(&varAuthTarget)); }
VariantClear(&varUserName); VariantClear(&varPassword); VariantClear(&varAuthTarget); break; }
case DISPID_HTTPREQUEST_OPEN: { VARIANT varMethod; VARIANT varUrl; VARIANT varAsync;
VariantInit(&varMethod); VariantInit(&varUrl); VariantInit(&varAsync);
hr = DispGetParam(pDispParams, 0, VT_BSTR, &varMethod, puArgErr);
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 1, VT_BSTR, &varUrl, puArgErr); }
if (SUCCEEDED(hr)) { hr = _DispGetOptionalParam(pDispParams, 2, VT_BOOL, &varAsync, puArgErr); }
if (SUCCEEDED(hr)) { hr = Open(V_BSTR(&varMethod), V_BSTR(&varUrl), varAsync); }
VariantClear(&varMethod); VariantClear(&varUrl); VariantClear(&varAsync);
break; }
case DISPID_HTTPREQUEST_SETREQUESTHEADER: { VARIANT varHeader; VARIANT varValue;
VariantInit(&varHeader); VariantInit(&varValue);
hr = DispGetParam(pDispParams, 0, VT_BSTR, &varHeader, puArgErr);
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 1, VT_BSTR, &varValue, puArgErr); }
if (SUCCEEDED(hr)) { hr = SetRequestHeader(V_BSTR(&varHeader), V_BSTR(&varValue)); }
VariantClear(&varHeader); VariantClear(&varValue);
break; }
case DISPID_HTTPREQUEST_GETRESPONSEHEADER: { VARIANT varHeader;
VariantInit(&varHeader);
hr = DispGetParam(pDispParams, 0, VT_BSTR, &varHeader, puArgErr);
if (SUCCEEDED(hr)) { BSTR bstrValue = NULL;
hr = GetResponseHeader(V_BSTR(&varHeader), &bstrValue);
if (SUCCEEDED(hr) && pVarResult) { V_VT(pVarResult) = VT_BSTR; V_BSTR(pVarResult) = bstrValue; } else SysFreeString(bstrValue); }
VariantClear(&varHeader);
break; }
case DISPID_HTTPREQUEST_GETALLRESPONSEHEADERS: { BSTR bstrResponseHeaders = NULL;
hr = GetAllResponseHeaders(&bstrResponseHeaders);
if (SUCCEEDED(hr) && pVarResult) { V_VT(pVarResult) = VT_BSTR; V_BSTR(pVarResult) = bstrResponseHeaders; } else SysFreeString(bstrResponseHeaders);
break; }
case DISPID_HTTPREQUEST_SEND: { if (pDispParams->cArgs <= 1) { VARIANT varEmptyBody;
VariantInit(&varEmptyBody);
hr = Send((pDispParams->cArgs == 0) ? varEmptyBody : pDispParams->rgvarg[0]); } else { hr = DISP_E_BADPARAMCOUNT; } break; }
case DISPID_HTTPREQUEST_STATUS: { long Status;
hr = get_Status(&Status);
if (SUCCEEDED(hr) && pVarResult) { V_VT(pVarResult) = VT_I4; V_I4(pVarResult) = Status; } break; }
case DISPID_HTTPREQUEST_STATUSTEXT: { BSTR bstrStatus = NULL;
hr = get_StatusText(&bstrStatus);
if (SUCCEEDED(hr) && pVarResult) { V_VT(pVarResult) = VT_BSTR; V_BSTR(pVarResult) = bstrStatus; } else SysFreeString(bstrStatus);
break; }
case DISPID_HTTPREQUEST_RESPONSETEXT: { BSTR bstrResponse = NULL;
hr = get_ResponseText(&bstrResponse);
if (SUCCEEDED(hr) && pVarResult) { V_VT(pVarResult) = VT_BSTR; V_BSTR(pVarResult) = bstrResponse; } else SysFreeString(bstrResponse);
break; }
case DISPID_HTTPREQUEST_RESPONSEBODY: { if (pVarResult) { hr = get_ResponseBody(pVarResult); } break; }
case DISPID_HTTPREQUEST_RESPONSESTREAM: { if (pVarResult) { hr = get_ResponseStream(pVarResult); } break; }
case DISPID_HTTPREQUEST_OPTION: { VARIANT varOption; WinHttpRequestOption Option;
VariantInit(&varOption);
hr = DispGetParam(pDispParams, 0, VT_I4, &varOption, puArgErr);
if (FAILED(hr)) break;
Option = static_cast<WinHttpRequestOption>(V_I4(&varOption));
if (wFlags & (DISPATCH_METHOD | DISPATCH_PROPERTYGET)) { if (pVarResult) { hr = get_Option(Option, pVarResult); } } else if (wFlags & DISPATCH_PROPERTYPUT) { hr = put_Option(Option, pDispParams->rgvarg[0]); }
VariantClear(&varOption); break; }
case DISPID_HTTPREQUEST_WAITFORRESPONSE: { VARIANT varTimeout; VARIANT_BOOL boolSucceeded;
VariantInit(&varTimeout);
hr = _DispGetOptionalParam(pDispParams, 0, VT_I4, &varTimeout, puArgErr);
if (SUCCEEDED(hr)) { hr = WaitForResponse(varTimeout, &boolSucceeded); }
if (pVarResult) { V_VT(pVarResult) = VT_BOOL; V_BOOL(pVarResult) = boolSucceeded; }
VariantClear(&varTimeout); break; }
case DISPID_HTTPREQUEST_SETTIMEOUTS: { VARIANT varResolveTimeout; VARIANT varConnectTimeout; VARIANT varSendTimeout; VARIANT varReceiveTimeout;
VariantInit(&varResolveTimeout); VariantInit(&varConnectTimeout); VariantInit(&varSendTimeout); VariantInit(&varReceiveTimeout);
hr = DispGetParam(pDispParams, 0, VT_I4, &varResolveTimeout, puArgErr);
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 1, VT_I4, &varConnectTimeout, puArgErr); }
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 2, VT_I4, &varSendTimeout, puArgErr); }
if (SUCCEEDED(hr)) { hr = DispGetParam(pDispParams, 3, VT_I4, &varReceiveTimeout, puArgErr); }
if (SUCCEEDED(hr)) { hr = SetTimeouts(V_I4(&varResolveTimeout), V_I4(&varConnectTimeout), V_I4(&varSendTimeout), V_I4(&varReceiveTimeout)); }
VariantClear(&varResolveTimeout); VariantClear(&varConnectTimeout); VariantClear(&varSendTimeout); VariantClear(&varReceiveTimeout); break; }
default: hr = DISP_E_MEMBERNOTFOUND; break; }
if (FAILED(hr) && (pExcepInfo != NULL)) { hr = FillExcepInfo(hr, pExcepInfo); }
return hr; }
static HRESULT FillExcepInfo(HRESULT hr, EXCEPINFO * pExcepInfo) { // Don't create excepinfo for these errors to mimic oleaut behavior.
if( hr == DISP_E_BADPARAMCOUNT || hr == DISP_E_NONAMEDARGS || hr == DISP_E_MEMBERNOTFOUND || hr == E_INVALIDARG) { return hr; }
// clear out exception info
IErrorInfo * pei = NULL;
pExcepInfo->wCode = 0; pExcepInfo->scode = hr;
// if error info exists, use it
GetErrorInfo(0, &pei); if (pei) { // give back to OLE
SetErrorInfo(0, pei);
pei->GetHelpContext(&pExcepInfo->dwHelpContext); pei->GetSource(&pExcepInfo->bstrSource); pei->GetDescription(&pExcepInfo->bstrDescription); pei->GetHelpFile(&pExcepInfo->bstrHelpFile);
// give complete ownership to OLEAUT
pei->Release();
hr = DISP_E_EXCEPTION; }
return hr;
}
STDMETHODIMP CHttpRequest::GetClassInfo(ITypeInfo ** ppTI) { if (!ppTI) return E_POINTER;
*ppTI = NULL; return GetHttpRequestTypeInfo(CLSID_WinHttpRequest, ppTI); }
STDMETHODIMP CHttpRequest::GetGUID(DWORD dwGuidKind, GUID * pGUID) { if (!pGUID) return E_POINTER;
if (dwGuidKind == GUIDKIND_DEFAULT_SOURCE_DISP_IID) { *pGUID = IID_IWinHttpRequestEvents; } else return E_INVALIDARG; return NOERROR; }
STDMETHODIMP CHttpRequest::EnumConnectionPoints(IEnumConnectionPoints **) { return E_NOTIMPL; }
STDMETHODIMP CHttpRequest::FindConnectionPoint(REFIID riid, IConnectionPoint ** ppCP) { if (!ppCP) return E_POINTER;
if (riid == IID_IWinHttpRequestEvents) { return _CP.QueryInterface(IID_IConnectionPoint, (void **)ppCP); } else return CONNECT_E_NOCONNECTION; }
STDMETHODIMP CHttpRequest::CHttpRequestEventsCP::QueryInterface(REFIID riid, void ** ppvObject) { if (!ppvObject) return E_INVALIDARG;
if (riid == IID_IUnknown || riid == IID_IConnectionPoint) { *ppvObject = static_cast<IUnknown *>(static_cast<IConnectionPoint *>(this)); AddRef(); return NOERROR; }
return E_NOINTERFACE; }
ULONG STDMETHODCALLTYPE CHttpRequest::CHttpRequestEventsCP::AddRef() { return Px()->AddRef(); }
ULONG STDMETHODCALLTYPE CHttpRequest::CHttpRequestEventsCP::Release() { return Px()->Release(); }
STDMETHODIMP CHttpRequest::CHttpRequestEventsCP::GetConnectionInterface(IID * pIID) { if (!pIID) return E_POINTER;
*pIID = IID_IWinHttpRequestEvents; return NOERROR; }
STDMETHODIMP CHttpRequest::CHttpRequestEventsCP::GetConnectionPointContainer ( IConnectionPointContainer ** ppCPC ) { if (!ppCPC) return E_POINTER;
return Px()->QueryInterface(IID_IConnectionPointContainer, (void **)ppCPC); }
STDMETHODIMP CHttpRequest::CHttpRequestEventsCP::Advise(IUnknown * pUnk, DWORD * pdwCookie) { if (!pUnk || !pdwCookie) { return E_POINTER; }
IWinHttpRequestEvents * pIWinHttpRequestEvents; HRESULT hr;
hr = pUnk->QueryInterface(IID_IWinHttpRequestEvents, (void **)&pIWinHttpRequestEvents);
if (SUCCEEDED(hr)) { *pdwCookie = _SinkArray.Add(static_cast<IUnknown *>(pIWinHttpRequestEvents)); if (*pdwCookie) { _cConnections++; hr = NOERROR; } else { hr = E_OUTOFMEMORY; } } else hr = CONNECT_E_CANNOTCONNECT;
return hr; }
STDMETHODIMP CHttpRequest::CHttpRequestEventsCP::Unadvise(DWORD dwCookie) { IUnknown * pSink = _SinkArray.GetUnknown(dwCookie);
if (pSink) { _SinkArray.Remove(dwCookie); pSink->Release(); --_cConnections; }
return NOERROR; }
STDMETHODIMP CHttpRequest::CHttpRequestEventsCP::EnumConnections(IEnumConnections **) { return E_NOTIMPL; }
void CHttpRequest::CHttpRequestEventsCP::FireOnResponseStart(long Status, BSTR ContentType) { if (_cConnections > 0 && !Px()->_bAborted) { GetSink()->OnResponseStart(Status, ContentType); } }
void CHttpRequest::CHttpRequestEventsCP::FireOnResponseDataAvailable ( const BYTE * rgbData, DWORD cbData ) { if (_cConnections > 0 && !Px()->_bAborted) { VARIANT varData; HRESULT hr; VariantInit(&varData);
hr = CreateVector(&varData, rgbData, cbData);
if (SUCCEEDED(hr)) { GetSink()->OnResponseDataAvailable(&V_ARRAY(&varData)); }
VariantClear(&varData); } }
void CHttpRequest::CHttpRequestEventsCP::FireOnResponseFinished() { if (_cConnections > 0 && !Px()->_bAborted) { GetSink()->OnResponseFinished(); } }
HRESULT CHttpRequest::CHttpRequestEventsCP::CreateEventSinksMarshaller() { HRESULT hr = NOERROR;
if (_cConnections > 0) { SafeRelease(_pSinkMarshaller); hr = CWinHttpRequestEventsMarshaller::Create(&_SinkArray, &_pSinkMarshaller); }
return hr; }
void CHttpRequest::CHttpRequestEventsCP::ShutdownEventSinksMarshaller() { if (_pSinkMarshaller) _pSinkMarshaller->Shutdown(); }
void CHttpRequest::CHttpRequestEventsCP::ReleaseEventSinksMarshaller() { SafeRelease(_pSinkMarshaller); }
void CHttpRequest::CHttpRequestEventsCP::FreezeEvents() { if (_pSinkMarshaller) _pSinkMarshaller->FreezeEvents(); }
void CHttpRequest::CHttpRequestEventsCP::UnfreezeEvents() { if (_pSinkMarshaller) _pSinkMarshaller->UnfreezeEvents(); }
CHttpRequest::CHttpRequestEventsCP::~CHttpRequestEventsCP() { // If any connections are still alive, unadvise them.
if (_cConnections > 0) { _SinkArray.ReleaseAll(); _cConnections = 0; } }
/*
* CHttpRequest::Initialize * * Purpose: * Zero all data members * */ void CHttpRequest::Initialize() { _cRefs = 0;
_pTypeInfo = NULL; _bstrUserAgent = NULL;
_dwProxySetting = INTERNET_OPEN_TYPE_PRECONFIG; _bstrProxyServer = NULL; _bstrBypassList = NULL;
_eState = CHttpRequest::CREATED;
_fAsync = FALSE; _hWorkerThread = NULL; _cRefsOnMainThread = 0; _dwMainThreadId = GetCurrentThreadId(); _hrAsyncResult = NOERROR;
_bAborted = false; _bSetTimeouts = false; _bDisableRedirects = false;
_hInet = NULL; _hConnection = NULL; _hHTTP = NULL;
_ResolveTimeout = 0; _ConnectTimeout = 0; _SendTimeout = 0; _ReceiveTimeout = 0;
_cbRequestBody = 0; _szRequestBuffer = NULL;
_dwCodePage = CP_UTF8; _dwEscapePercentFlag = 0;
_szResponseBuffer = NULL; _cbResponseBuffer = 0; _cbResponseBody = 0;
_hAbortedConnectObject = NULL; _hAbortedRequestObject = NULL;
_bstrCertSubject = NULL; _dwSslIgnoreFlags = 0; }
/*
* CHttpRequest::ReleaseResources * * Purpose: * Release all handles, events, and buffers * */ void CHttpRequest::ReleaseResources() { SafeRelease(_pTypeInfo);
if (_hWorkerThread) { CloseHandle(_hWorkerThread); _hWorkerThread = NULL; }
_CP.ReleaseEventSinksMarshaller();
//
// Derefence aborted handle objects (if any).
//
if (_hAbortedRequestObject != NULL) { DereferenceObject(_hAbortedRequestObject); _hAbortedRequestObject = NULL; }
if (_hAbortedConnectObject != NULL) { DereferenceObject(_hAbortedConnectObject); _hAbortedConnectObject = NULL; }
if (_hHTTP) { HINTERNET temp = _hHTTP; _hHTTP = NULL; WinHttpCloseHandle(temp); }
if (_hConnection) { HINTERNET temp = _hConnection; _hConnection = NULL; WinHttpCloseHandle(temp); }
if (_hInet) { HINTERNET temp = _hInet; _hInet = NULL; WinHttpCloseHandle(temp); }
if (_szRequestBuffer) { delete [] _szRequestBuffer; _szRequestBuffer = NULL; }
if (_szResponseBuffer) { delete [] _szResponseBuffer; _szResponseBuffer = NULL; }
if (_bstrUserAgent) { SysFreeString(_bstrUserAgent); _bstrUserAgent = NULL; }
if (_bstrProxyServer) { SysFreeString(_bstrProxyServer); _bstrProxyServer = NULL; }
if (_bstrBypassList) { SysFreeString(_bstrBypassList); _bstrBypassList = NULL; }
if (_bstrCertSubject) { SysFreeString(_bstrCertSubject); _bstrCertSubject = NULL; } }
/*
* CHttpRequest::Reset * * Purpose: * Release all resources and initialize data members * */
void CHttpRequest::Reset() { ReleaseResources(); Initialize(); }
/*
* CHttpRequest::Recycle * * Purpose: * Recycle object * */
void CHttpRequest::Recycle() { //
// Wait for the worker thread to shut down. This shouldn't take long
// since the Abort will close the Request and Connection handles.
//
if (_hWorkerThread) { DWORD dwWaitResult;
for (;;) { dwWaitResult = MsgWaitForMultipleObjects(1, &_hWorkerThread, FALSE, INFINITE, QS_ALLINPUT);
if (dwWaitResult == (WAIT_OBJECT_0 + 1)) { // Message waiting in the message queue.
// Run message pump to clear queue.
MessageLoop(); } else { break; } }
CloseHandle(_hWorkerThread); _hWorkerThread = NULL; }
_hConnection = NULL; _hHTTP = NULL;
//
// Derefence aborted handle objects (if any).
//
if (_hAbortedRequestObject != NULL) { DereferenceObject(_hAbortedRequestObject); _hAbortedRequestObject = NULL; }
if (_hAbortedConnectObject != NULL) { DereferenceObject(_hAbortedConnectObject); _hAbortedConnectObject = NULL; }
_fAsync = FALSE; _hrAsyncResult = NOERROR;
_bAborted = false;
// don't reset timeouts, keep any that were set.
_cbRequestBody = 0; _cbResponseBuffer = 0; _cbResponseBody = 0;
if (_szRequestBuffer) { delete [] _szRequestBuffer; _szRequestBuffer = NULL; }
if (_szResponseBuffer) { delete [] _szResponseBuffer; _szResponseBuffer = NULL; }
_CP.ShutdownEventSinksMarshaller();
_CP.ReleaseEventSinksMarshaller();
// Allow events to fire; Abort() would have frozen them from firing.
_CP.UnfreezeEvents();
SetState(CHttpRequest::CREATED); }
/*
* CHttpRequest::ReadResponse * * Purpose: * Read the response bits * * Parameters: * None * * Errors: * E_FAIL * E_OUTOFMEMORY */ HRESULT CHttpRequest::ReadResponse() { HRESULT hr = NOERROR; BOOL fRetCode; long lStatus; BSTR bstrContentType = NULL; DWORD dwContentLength = 0;
// Determine the content length
DWORD cb = sizeof(dwContentLength);
fRetCode = HttpQueryInfoA( _hHTTP, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &dwContentLength, &cb, 0);
hr = get_Status(&lStatus);
if (FAILED(hr)) goto Error;
hr = _GetResponseHeader(L"Content-Type", &bstrContentType);
if (FAILED(hr)) { bstrContentType = SysAllocString(L""); if (bstrContentType == NULL) goto ErrorOutOfMemory;
hr = NOERROR; }
_CP.FireOnResponseStart(lStatus, bstrContentType);
if (dwContentLength != 0) { _szResponseBuffer = New char[dwContentLength];
if (_szResponseBuffer) { _cbResponseBuffer = dwContentLength; } else goto ErrorOutOfMemory; } else { _szResponseBuffer = NULL; _cbResponseBuffer = 0; }
//
// Read data until there is no more - we need to buffer the data
//
while (!_bAborted) { DWORD cbAvail = 0; DWORD cbRead = 0;
fRetCode = WinHttpQueryDataAvailable(_hHTTP, &cbAvail);
if (!fRetCode) { goto ErrorFail; }
// Check for buffer overflow - dynamically resize if neccessary
if (_cbResponseBody + cbAvail > _cbResponseBuffer) { ULONG cbNewSize = _cbResponseBody + cbAvail; char * szNewBuf = New char[cbNewSize];
if (!szNewBuf) goto ErrorOutOfMemory;
if (_szResponseBuffer) { ::memcpy(szNewBuf, _szResponseBuffer, _cbResponseBody); delete [] _szResponseBuffer; }
_cbResponseBuffer = cbNewSize;
_szResponseBuffer = szNewBuf; }
fRetCode = WinHttpReadData( _hHTTP, &_szResponseBuffer[_cbResponseBody], cbAvail, &cbRead);
if (!fRetCode) { goto ErrorFail; } // No more data
if (cbRead == 0) break;
_CP.FireOnResponseDataAvailable((const BYTE *)&_szResponseBuffer[_cbResponseBody], cbRead);
_cbResponseBody += cbRead; }
SetState(CHttpRequest::RESPONSE);
hr = NOERROR;
Cleanup: if (bstrContentType) SysFreeString(bstrContentType);
_CP.FireOnResponseFinished();
return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup;
Error: goto Cleanup; }
STDMETHODIMP CHttpRequest::SetProxy(HTTPREQUEST_PROXY_SETTING ProxySetting, VARIANT varProxyServer, VARIANT varBypassList) { HRESULT hr = NOERROR;
if (!IsValidVariant(varProxyServer) || !IsValidVariant(varBypassList)) return E_INVALIDARG;
if (_bstrProxyServer) { SysFreeString(_bstrProxyServer); _bstrProxyServer = NULL; }
if (_bstrBypassList) { SysFreeString(_bstrBypassList); _bstrBypassList = NULL; }
switch (ProxySetting) { case HTTPREQUEST_PROXYSETTING_PRECONFIG: _dwProxySetting = INTERNET_OPEN_TYPE_PRECONFIG; break;
case HTTPREQUEST_PROXYSETTING_DIRECT: _dwProxySetting = INTERNET_OPEN_TYPE_DIRECT; break;
case HTTPREQUEST_PROXYSETTING_PROXY: _dwProxySetting = INTERNET_OPEN_TYPE_PROXY; _bstrProxyServer = GetBSTRFromVariant(varProxyServer); _bstrBypassList = GetBSTRFromVariant(varBypassList); break;
default: hr = E_INVALIDARG; break; }
if (SUCCEEDED(hr)) { if (_hInet) { WINHTTP_PROXY_INFOW ProxyInfo;
memset(&ProxyInfo, 0, sizeof(ProxyInfo));
ProxyInfo.dwAccessType = _dwProxySetting; ProxyInfo.lpszProxy = _bstrProxyServer; ProxyInfo.lpszProxyBypass = _bstrBypassList; if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_PROXY, &ProxyInfo, sizeof(ProxyInfo))) { hr = HRESULT_FROM_WIN32(GetLastError()); } } }
SetErrorInfo(hr);
return hr; }
STDMETHODIMP CHttpRequest::SetCredentials( BSTR bstrUserName, BSTR bstrPassword, HTTPREQUEST_SETCREDENTIALS_FLAGS Flags) { HRESULT hr;
// Must call Open method before SetCredentials.
if (! _hHTTP) { goto ErrorCannotCallBeforeOpen; }
if (!IsValidBstr(bstrUserName) || !IsValidBstr(bstrPassword)) return E_INVALIDARG;
if (Flags == HTTPREQUEST_SETCREDENTIALS_FOR_SERVER) { // Set Username and Password.
WinHttpSetOption( _hHTTP, WINHTTP_OPTION_USERNAME, bstrUserName, SysStringLen(bstrUserName));
WinHttpSetOption( _hHTTP, WINHTTP_OPTION_PASSWORD, bstrPassword, SysStringLen(bstrPassword)); } else if (Flags == HTTPREQUEST_SETCREDENTIALS_FOR_PROXY) { // Set Username and Password.
WinHttpSetOption( _hHTTP, WINHTTP_OPTION_PROXY_USERNAME, bstrUserName, SysStringLen(bstrUserName));
WinHttpSetOption( _hHTTP, WINHTTP_OPTION_PROXY_PASSWORD, bstrPassword, SysStringLen(bstrPassword)); } else return E_INVALIDARG;
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeOpen: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN); goto Cleanup; }
/*
* CHttpRequest::Open * * Purpose: * Open a logical HTTP connection * * Parameters: * bstrMethod IN HTTP method (GET, PUT, ...) * bstrUrl IN Target URL * * Errors: * E_FAIL * E_INVALIDARG * E_OUTOFMEMORY * E_ACCESSDENIED * Errors from InternetOpenA and WinHttpCrackUrlA and InternetConnectA * and HttpOpenRequestA */
STDMETHODIMP CHttpRequest::Open( BSTR bstrMethod, BSTR bstrUrl, VARIANT varAsync) { HRESULT hr = NOERROR; BSTR bstrHostName = NULL; BSTR bstrUrlPath = NULL; DWORD dwHttpOpenFlags = 0; URL_COMPONENTSW url;
// Check for reinitialization
if (_eState != CHttpRequest::CREATED) { //
// Abort any request in progress.
// This will also recycle the object.
//
Abort(); }
// Validate parameters
if (!bstrMethod || !bstrUrl || !IsValidBstr(bstrMethod) || !IsValidBstr(bstrUrl) || !lstrlenW(bstrMethod) || // cannot have empty method
!lstrlenW(bstrUrl) || // cannot have empty url
!IsValidVariant(varAsync)) return E_INVALIDARG;
_fAsync = GetBoolFromVariant(varAsync, FALSE);
//
// Open an Internet Session if one does not already exist.
//
if (!_hInet) { _hInet = WinHttpOpen( GetUserAgentString(), _dwProxySetting, _bstrProxyServer, _bstrBypassList, 0);
if (!_hInet) goto ErrorFail;
// In winhttp5, this should be adjusted through an exposed option
// or flag, rather than going through the back door.
HINTERNET hSessionMapped = NULL; if (ERROR_SUCCESS == MapHandleToAddress(_hInet, (LPVOID *)&hSessionMapped, FALSE) && hSessionMapped) { ((INTERNET_HANDLE_OBJECT *)hSessionMapped)->SetUseSslSessionCache(TRUE); DereferenceObject(hSessionMapped); } }
//
// If any timeouts were set previously, apply them.
//
if (_bSetTimeouts) { if (!WinHttpSetTimeouts(_hInet, (int)_ResolveTimeout, (int)_ConnectTimeout, (int)_SendTimeout, (int)_ReceiveTimeout)) goto ErrorFail; }
//
// Set the code page on the Session handle; the Connect
// handle will also inherit this value.
//
if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_CODEPAGE, &_dwCodePage, sizeof(_dwCodePage))) goto ErrorFail;
// Break the URL into the required components
ZeroMemory(&url, sizeof(URL_COMPONENTSW));
url.dwStructSize = sizeof(URL_COMPONENTSW); url.dwHostNameLength = 1; url.dwUrlPathLength = 1; url.dwExtraInfoLength = 1;
if (!WinHttpCrackUrl(bstrUrl, 0, 0, &url)) goto ErrorFail;
// Check for non-http schemes
if (url.nScheme != INTERNET_SCHEME_HTTP && url.nScheme != INTERNET_SCHEME_HTTPS) goto ErrorUnsupportedScheme;
// IE6/Reno Bug #6236: if the client does not specify a resource path,
// then add the "/".
if (url.dwUrlPathLength == 0) { INET_ASSERT(url.dwExtraInfoLength == 1);
url.lpszUrlPath = L"/"; url.dwUrlPathLength = 1; }
bstrHostName = SysAllocStringLen(url.lpszHostName, url.dwHostNameLength); bstrUrlPath = SysAllocStringLen(url.lpszUrlPath, lstrlenW(url.lpszUrlPath));
if (!bstrHostName || !bstrUrlPath) goto ErrorOutOfMemory;
INET_ASSERT(_hConnection == NULL); INET_ASSERT(_hHTTP == NULL);
_hConnection = WinHttpConnect( _hInet, bstrHostName, url.nPort, 0);
if (!_hConnection) goto ErrorFail;
if (url.nScheme == INTERNET_SCHEME_HTTPS) { dwHttpOpenFlags |= WINHTTP_FLAG_SECURE; }
//
// Apply EscapePercentInURL option.
//
dwHttpOpenFlags |= _dwEscapePercentFlag;
_hHTTP = WinHttpOpenRequest( _hConnection, bstrMethod, bstrUrlPath, NULL, NULL, NULL, dwHttpOpenFlags);
if (!_hHTTP) goto ErrorFail;
// Set the SSL ignore flags through an undocumented front door
if (_dwSslIgnoreFlags) { WinHttpSetOption(_hHTTP, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&_dwSslIgnoreFlags, sizeof(_dwSslIgnoreFlags)); }
SetState(CHttpRequest::OPENED);
hr = NOERROR;
Cleanup: if (bstrHostName) SysFreeString(bstrHostName);; if (bstrUrlPath) SysFreeString(bstrUrlPath);
SetErrorInfo(hr);
return hr;
ErrorUnsupportedScheme: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_UNRECOGNIZED_SCHEME); goto Cleanup;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup;
Error: goto Cleanup; }
/*
* CHttpRequest::SetRequestHeader * * Purpose: * Set a request header * * Parameters: * bstrHeader IN HTTP request header * bstrValue IN Header value * * Errors: * E_FAIL * E_INVALIDARG * E_UNEXPECTED */
STDMETHODIMP CHttpRequest::SetRequestHeader(BSTR bstrHeader, BSTR bstrValue) { WCHAR * wszHeaderValue = NULL; DWORD cchHeaderValue; DWORD dwModifiers = HTTP_ADDREQ_FLAG_ADD; HRESULT hr = NOERROR;
// Validate header parameter (null or zero-length value is allowed)
if (!bstrHeader || !IsValidBstr(bstrHeader) || lstrlenW(bstrHeader)==0 || !IsValidBstr(bstrValue)) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::OPENED) goto ErrorCannotCallBeforeOpen; else if (_eState >= CHttpRequest::SENDING) goto ErrorCannotCallAfterSend;
// Ignore attempts to set the Content-Length header; the
// content length is computed and sent automatically.
if (StrCmpIW(bstrHeader, L"Content-Length") == 0) goto Cleanup;
cchHeaderValue = SysStringLen(bstrHeader) + SysStringLen(bstrValue) + 2 /* wcslen(L": ") */ + 2 /* wcslen(L"\r\n") */;
wszHeaderValue = New WCHAR [cchHeaderValue + 1];
if (!wszHeaderValue) goto ErrorOutOfMemory;
wcscpy(wszHeaderValue, bstrHeader); wcscat(wszHeaderValue, L": "); if (bstrValue) wcscat(wszHeaderValue, bstrValue); wcscat(wszHeaderValue, L"\r\n");
// For blank header values, erase the header by setting the
// REPLACE flag.
if (SysStringLen(bstrValue) == 0) { dwModifiers |= HTTP_ADDREQ_FLAG_REPLACE; } if (! WinHttpAddRequestHeaders(_hHTTP, wszHeaderValue, -1L, dwModifiers)) goto ErrorFail;
hr = NOERROR;
Cleanup: if (wszHeaderValue) delete [] wszHeaderValue;
SetErrorInfo(hr); return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorCannotCallBeforeOpen: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN); goto Error;
ErrorCannotCallAfterSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND); goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
Error: goto Cleanup; }
/*
* CHttpRequest::SetRequiredRequestHeaders * * Purpose: * Set implicit request headers * * Parameters: * None * * Errors: * E_FAIL * E_UNEXPECTED * E_OUTOFMEMORY * Errors from WinHttpAddRequestHeaders and WinHttpSendRequest */
HRESULT CHttpRequest::SetRequiredRequestHeaders() { HRESULT hr = NOERROR; char szContentLengthHeader[sizeof("Content-Length") + sizeof(": ") + 15 + // content-length value
sizeof("\r\n") + 1];
lstrcpy(szContentLengthHeader, "Content-Length: "); _ltoa(_cbRequestBody, szContentLengthHeader + 16, /* "Content-Length: " */ 10); lstrcat(szContentLengthHeader, "\r\n");
if (! HttpAddRequestHeadersA(_hHTTP, szContentLengthHeader, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE)) { hr = E_FAIL; }
// Add an Accept: */* header if no other Accept header
// has been set. Ignore any return code, since it is
// not fatal if this fails.
HttpAddRequestHeadersA(_hHTTP, "Accept: */*", -1L, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
return hr; }
DWORD WINAPI WinHttpRequestSendAsync(LPVOID lpParameter) { #ifdef WINHTTP_FOR_MSXML
//
// MSXML needs to initialize its thread local storage data.
// It does not do this during DLL_THREAD_ATTACH, so our
// worker thread must explicitly call into MSXML to initialize
// its TLS for this thread.
//
InitializeMsxmlTLS(); #endif
HRESULT hr; DWORD dwExitCode;
CHttpRequest * pWinHttpRequest = reinterpret_cast<CHttpRequest *>(lpParameter);
INET_ASSERT(pWinHttpRequest != NULL);
dwExitCode = pWinHttpRequest->SendAsync();
pWinHttpRequest->Release();
// If this worker thread was impersonating, revert to the default
// process identity.
RevertToSelf();
return dwExitCode; }
/*
* CHttpRequest::CreateAsyncWorkerThread * */
HRESULT CHttpRequest::CreateAsyncWorkerThread() { DWORD dwWorkerThreadId; HANDLE hThreadToken = NULL; HRESULT hr;
hr = _CP.CreateEventSinksMarshaller();
if (FAILED(hr)) return hr;
hr = NOERROR;
//
// If the current thread is impersonating, then grab its access token
// and revert the current thread (so it is nolonger impersonating).
// After creating the worker thread, we will make the main thread
// impersonate again. Apparently you should not call CreateThread
// while impersonating.
//
if (OpenThreadToken(GetCurrentThread(), (TOKEN_IMPERSONATE | TOKEN_READ), FALSE, &hThreadToken)) { INET_ASSERT(hThreadToken != 0);
RevertToSelf(); }
// Create the worker thread suspended.
_hWorkerThread = CreateThread(NULL, 0, WinHttpRequestSendAsync, (void *)static_cast<CHttpRequest *>(this), CREATE_SUSPENDED, &dwWorkerThreadId);
// If CreateThread fails, grab the error code now.
if (!_hWorkerThread) { hr = HRESULT_FROM_WIN32(GetLastError()); }
//
// If the main thread was impersonating, then:
// (1) have the worker thread impersonate the same user, and
// (2) have the main thread resume impersonating the
// client too (since we called RevertToSelf above).
//
if (hThreadToken) { if (_hWorkerThread) { SetThreadToken(&_hWorkerThread, hThreadToken); }
SetThreadToken(NULL, hThreadToken); CloseHandle(hThreadToken); }
// If the worker thread was created, start it running.
if (_hWorkerThread) { // The worker thread owns a ref count on the component.
// Don't call AddRef() as it will attribute the ref count
// to the main thread.
_cRefs++;
ResumeThread(_hWorkerThread); } else { _CP.ShutdownEventSinksMarshaller(); _CP.ReleaseEventSinksMarshaller(); }
return hr; }
/*
* CHttpRequest::Send * * Purpose: * Send the HTTP request * * Parameters: * varBody IN Request body * * Errors: * E_FAIL * E_UNEXPECTED * E_OUTOFMEMORY * Errors from WinHttpAddRequestHeaders and WinHttpSendRequest */
STDMETHODIMP CHttpRequest::Send(VARIANT varBody) { HRESULT hr = NOERROR; BOOL fRetCode = FALSE; BOOL fRetryWithClientAuth = TRUE;
// Validate parameter
if (!IsValidVariant(varBody)) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::OPENED) goto ErrorCannotCallBeforeOpen;
// Get the request body
hr = SetRequestBody(varBody);
if (FAILED(hr)) goto Error;
hr = SetRequiredRequestHeaders();
if (FAILED(hr)) goto Error;
if (_bDisableRedirects) { DWORD dwDisable = WINHTTP_DISABLE_REDIRECTS;
WinHttpSetOption(_hHTTP, WINHTTP_OPTION_DISABLE_FEATURE, (void *) &dwDisable, sizeof(DWORD)); }
try_again: SetState(CHttpRequest::SENDING); if (_fAsync) { hr = CreateAsyncWorkerThread();
if (FAILED(hr)) goto Error; } else { // Send the HTTP request
fRetCode = WinHttpSendRequest( _hHTTP, NULL, 0, // No header info here
_szRequestBuffer, _cbRequestBody, _cbRequestBody, 0);
if (!fRetCode) { goto ErrorFail; }
fRetCode = WinHttpReceiveResponse(_hHTTP, NULL);
if (!fRetCode) { goto ErrorFail; }
SetState(CHttpRequest::SENT);
// Read the response data
hr = ReadResponse();
if (FAILED(hr)) goto Error; }
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeOpen: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN); goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); if (!_fAsync && hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED) && fRetryWithClientAuth) { fRetryWithClientAuth = FALSE; // Try to enumerate the first cert in the reverted user context,
// select the cert (per object sesssion, not global), and send
// the request again.
if (SelectCertificate()) goto try_again; } goto Cleanup;
Error: goto Cleanup; }
/*
* CHttpRequest::SendAsync * * Purpose: * Send the HTTP request * * Parameters: * varBody IN Request body * * Errors: * E_FAIL * E_UNEXPECTED * E_OUTOFMEMORY * Errors from WinHttpAddRequestHeaders and WinHttpSendRequest */
DWORD CHttpRequest::SendAsync() { DWORD dwLastError = 0; DWORD fRetCode; HRESULT hr; BOOL fRetryWithClientAuth = TRUE;
try_again: if (_bAborted || !_hHTTP) goto ErrorUnexpected;
// Send the HTTP request
fRetCode = WinHttpSendRequest( _hHTTP, NULL, 0, // No header info here
_szRequestBuffer, _cbRequestBody, _cbRequestBody, 0);
if (!fRetCode) goto ErrorFail;
fRetCode = WinHttpReceiveResponse(_hHTTP, NULL);
if (!fRetCode) goto ErrorFail;
if (!_bAborted) { SetState(CHttpRequest::SENT);
hr = ReadResponse();
if (FAILED(hr)) { if (hr == E_OUTOFMEMORY) goto ErrorOutOfMemory; goto ErrorFail; } }
hr = NOERROR;
Cleanup: _hrAsyncResult = hr; return dwLastError;
ErrorUnexpected: dwLastError = ERROR_WINHTTP_INTERNAL_ERROR; hr = HRESULT_FROM_WIN32(dwLastError); goto Cleanup;
ErrorFail: dwLastError = GetLastError(); if (dwLastError == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED && fRetryWithClientAuth) { fRetryWithClientAuth = FALSE; // Try to enumerate the first cert in the reverted user context,
// select the cert (per object sesssion, not global), and send
// the request again.
if (SelectCertificate()) { SetState(CHttpRequest::SENDING); goto try_again; } } hr = HRESULT_FROM_WIN32(dwLastError); goto Cleanup;
ErrorOutOfMemory: dwLastError = ERROR_NOT_ENOUGH_MEMORY; hr = E_OUTOFMEMORY; goto Cleanup; }
STDMETHODIMP CHttpRequest::WaitForResponse(VARIANT varTimeout, VARIANT_BOOL * pboolSucceeded) { HRESULT hr = NOERROR; bool bSucceeded= true; DWORD dwTimeout;
// Validate parameters; null pboolSucceeded pointer is Ok.
if (!IsValidVariant(varTimeout) || (pboolSucceeded && IsBadWritePtr(pboolSucceeded, sizeof(VARIANT_BOOL)))) return E_INVALIDARG;
// Get the timeout value. Disallow numbers
// less than -1 (which means INFINITE).
if (GetLongFromVariant(varTimeout, INFINITE) < -1L) return E_INVALIDARG;
dwTimeout = GetDwordFromVariant(varTimeout, INFINITE);
// Validate state.
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend;
//
// WaitForResponse is a no-op if we're not in async mode.
//
if (_fAsync && _hWorkerThread) { //
// Has the worker thread has already finished?
//
if (WaitForSingleObject(_hWorkerThread, 0) == WAIT_TIMEOUT) { //
// Convert Timeout from seconds to milliseconds. Any timeout
// value over 4 million seconds (~46 days) is "rounded up"
// to INFINITE. :)
//
if (dwTimeout > 4000000) // avoid overflow
{ dwTimeout = INFINITE; } else { // convert to milliseconds
dwTimeout *= 1000; }
DWORD dwStartTime; DWORD dwWaitResult; bool bWaitAgain; do { dwStartTime = GetTickCount();
dwWaitResult = MsgWaitForMultipleObjects(1, &_hWorkerThread, FALSE, dwTimeout, QS_ALLINPUT); bWaitAgain = false;
switch (dwWaitResult) { case WAIT_OBJECT_0: // Thread exited.
MessageLoop(); hr = _hrAsyncResult; bSucceeded = SUCCEEDED(hr); break; case WAIT_OBJECT_0 + 1: // Message waiting in the message queue.
// Run message pump to clear queue.
MessageLoop(); bWaitAgain = true; break; case WAIT_TIMEOUT: // Timeout.
bSucceeded = false; break; case (-1): default: // Error.
goto ErrorFail; break; }
// If we're going to continue waiting for the worker
// thread, decrease timeout appropriately.
if (bWaitAgain) { dwTimeout = UpdateTimeout(dwTimeout, dwStartTime); } } while (bWaitAgain); } else { // If the worker thread is already done, then pump messages
// to clear any events that it may have posted.
MessageLoop(); hr = _hrAsyncResult; bSucceeded = SUCCEEDED(hr); } }
Cleanup: if (pboolSucceeded) { *pboolSucceeded = (bSucceeded ? VARIANT_TRUE : VARIANT_FALSE); }
SetErrorInfo(hr); return hr;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
Error: bSucceeded = false; goto Cleanup; }
STDMETHODIMP CHttpRequest::Abort() { //
// Abort if not already aborted and not in the CREATED state,
// (meaning at least the Open method has been called).
//
if ((_eState > CHttpRequest::CREATED) && !_bAborted) { DWORD error;
_bAborted = true;
// Tell our connection point manager to abort any
// events "in flight"--i.e., abort any events that
// may have already been posted by the worker thread.
_CP.FreezeEvents();
if (_hHTTP) { //
// Add a ref count on the HTTP Request handle.
//
INET_ASSERT(_hAbortedRequestObject == NULL); error = MapHandleToAddress(_hHTTP, (LPVOID *)&_hAbortedRequestObject, FALSE); INET_ASSERT(error == 0);
WinHttpCloseHandle(_hHTTP); }
if (_hConnection) { //
// Add a ref count on the Connection handle.
//
INET_ASSERT(_hAbortedConnectObject == NULL); error = MapHandleToAddress(_hConnection, (LPVOID *)&_hAbortedConnectObject, FALSE); INET_ASSERT(error == 0);
WinHttpCloseHandle(_hConnection); }
// Recycle the object.
Recycle(); }
return NOERROR; }
STDMETHODIMP CHttpRequest::SetTimeouts(long ResolveTimeout, long ConnectTimeout, long SendTimeout, long ReceiveTimeout) { if ((ResolveTimeout < -1L) || (ConnectTimeout < -1L) || (SendTimeout < -1L) || (ReceiveTimeout < -1L)) { return E_INVALIDARG; }
HRESULT hr = NOERROR;
_ResolveTimeout = (DWORD) ResolveTimeout; _ConnectTimeout = (DWORD) ConnectTimeout; _SendTimeout = (DWORD) SendTimeout; _ReceiveTimeout = (DWORD) ReceiveTimeout; _bSetTimeouts = true;
if (_hHTTP) { DWORD fRetCode;
fRetCode = WinHttpSetTimeouts(_hHTTP, (int)_ResolveTimeout, (int)_ConnectTimeout, (int)_SendTimeout, (int)_ReceiveTimeout);
if (!fRetCode) hr = E_INVALIDARG; }
return hr; }
/*
* CHttpRequest::SetRequestBody * * Purpose: * Set the request body * * Parameters: * varBody IN Request body * * Errors: * E_FAIL * E_OUTOFMEMORY * E_UNEXPECTED */
HRESULT CHttpRequest::SetRequestBody(VARIANT varBody) { HRESULT hr = NOERROR; VARIANT varTemp; BSTR bstrBody = NULL; SAFEARRAY * psa = NULL; VARIANT * pvarBody = NULL; IStream * pStm = NULL;
// varBody is validated by CHttpRequest::Send().
VariantInit(&varTemp);
// Free a previously set body and its response
if (_szRequestBuffer) { delete [] _szRequestBuffer; _szRequestBuffer = NULL; _cbRequestBody = 0; }
if (_szResponseBuffer) { delete [] _szResponseBuffer; _szResponseBuffer = NULL; _cbResponseBuffer = 0; _cbResponseBody = 0; }
if (V_ISBYREF(&varBody)) { pvarBody = varBody.pvarVal; } else { pvarBody = &varBody; }
// Check for an empty body
if (V_VT(pvarBody) == VT_EMPTY || V_VT(pvarBody) == VT_NULL || V_VT(pvarBody) == VT_ERROR) goto Cleanup;
//
// Extract the body: BSTR or array of UI1
//
// We need to explicitly look for the byte array since it will be converted
// to a BSTR by VariantChangeType.
if (V_ISARRAY(pvarBody) && (V_VT(pvarBody) & VT_UI1)) { BYTE * pb = NULL; long lUBound = 0; long lLBound = 0;
psa = V_ARRAY(pvarBody);
// We only handle 1 dimensional arrays
if (SafeArrayGetDim(psa) != 1) goto ErrorFail;
// Get access to the blob
hr = SafeArrayAccessData(psa, (void **)&pb);
if (FAILED(hr)) goto Error;
// Compute the data size from the upper and lower array bounds
hr = SafeArrayGetLBound(psa, 1, &lLBound);
if (FAILED(hr)) goto Error;
hr = SafeArrayGetUBound(psa, 1, &lUBound);
if (FAILED(hr)) goto Error;
_cbRequestBody = lUBound - lLBound + 1;
if (_cbRequestBody > 0) { // Copy the data into the request buffer
_szRequestBuffer = New char [_cbRequestBody];
if (!_szRequestBuffer) { _cbRequestBody = 0; goto ErrorOutOfMemory; } ::memcpy(_szRequestBuffer, pb, _cbRequestBody); } SafeArrayUnaccessData(psa); psa = NULL; } else { // Try a BSTR
bstrBody = GetBSTRFromVariant(*pvarBody);
if (bstrBody) { hr = BSTRToUTF8(&_szRequestBuffer, &_cbRequestBody, bstrBody);
if (FAILED(hr)) goto Error; } else { // Try a Stream
if (V_VT(pvarBody) == VT_UNKNOWN || V_VT(pvarBody) == VT_DISPATCH) { IStream * pStm;
hr = pvarBody->punkVal->QueryInterface( IID_IStream, (void **)&pStm);
if (FAILED(hr)) goto Error;
hr = ReadFromStream( &_szRequestBuffer, &_cbRequestBody, pStm);
pStm->Release();
if (FAILED(hr)) goto Error; } } }
hr = NOERROR;
Cleanup: VariantClear(&varTemp); if (psa) SafeArrayUnaccessData(psa); if (bstrBody) SysFreeString(bstrBody); return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
Error: goto Cleanup; }
/*
* CHttpRequest::GetResponseHeader * * Purpose: * Get a response header * * Parameters: * bstrHeader IN HTTP request header * pbstrValue OUT Header value * * Errors: * E_FAIL * E_INVALIDARG * E_OUTOFMEMORY * E_UNEXPECTED * Errors from WinHttpQueryHeaders */
STDMETHODIMP CHttpRequest::GetResponseHeader(BSTR bstrHeader, BSTR * pbstrValue) { return _GetResponseHeader(bstrHeader, pbstrValue); }
HRESULT CHttpRequest::_GetResponseHeader(OLECHAR * wszHeader, BSTR * pbstrValue) { HRESULT hr = NOERROR; WCHAR * wszHeaderValue = NULL; DWORD cb; BOOL fRetCode;
// Validate parameters
if (IsBadReadPtr(wszHeader, 2) || IsBadWritePtr(pbstrValue, sizeof(BSTR)) || !lstrlenW(wszHeader)) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend;
*pbstrValue = NULL;
cb = 64; // arbitrary size in which many header values will fit
wszHeaderValue = New WCHAR[cb];
if (!wszHeaderValue) goto ErrorOutOfMemory;
RetryQuery: // Determine length of requested header
fRetCode = WinHttpQueryHeaders( _hHTTP, HTTP_QUERY_CUSTOM, wszHeader, wszHeaderValue, &cb, 0);
// Check for ERROR_INSUFFICIENT_BUFFER - reallocate the buffer and retry
if (!fRetCode) { switch (GetLastError()) { case ERROR_INSUFFICIENT_BUFFER: { delete [] wszHeaderValue; wszHeaderValue = New WCHAR[cb]; // should this be cb/2?
if (!wszHeaderValue) goto ErrorOutOfMemory; goto RetryQuery; }
case ERROR_HTTP_HEADER_NOT_FOUND: goto ErrorFail; default: goto ErrorFail; } }
*pbstrValue = SysAllocString(wszHeaderValue);
if (!*pbstrValue) goto ErrorOutOfMemory;
hr = NOERROR;
Cleanup: if (wszHeaderValue) delete [] wszHeaderValue;
SetErrorInfo(hr); return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
Error: goto Cleanup; }
/*
* CHttpRequest::GetAllResponseHeaders * * Purpose: * Return the response headers * * Parameters: * pbstrHeaders IN/OUT CRLF delimited headers * * Errors: * E_FAIL * E_INVALIDARG * E_OUTOFMEMORY * E_UNEXPECTED */
STDMETHODIMP CHttpRequest::GetAllResponseHeaders(BSTR * pbstrHeaders) { HRESULT hr = NOERROR; BOOL fRetCode; WCHAR * wszAllHeaders = NULL; WCHAR * wszFirstHeader = NULL; DWORD cb = 0;
// Validate parameter
if (IsBadWritePtr(pbstrHeaders, sizeof(BSTR))) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend;
*pbstrHeaders = NULL;
RetryQuery: // Determine the length of all headers and then get all the headers
fRetCode = WinHttpQueryHeaders( _hHTTP, HTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, wszAllHeaders, &cb, 0);
if (!fRetCode) { // Allocate a buffer large enough to hold all headers
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { wszAllHeaders = New WCHAR[cb];
if (!wszAllHeaders) goto ErrorOutOfMemory;
goto RetryQuery; } else { goto ErrorFail; } }
// Bypass status line - jump past first CRLF (0x13, 0x10)
wszFirstHeader = wcschr(wszAllHeaders, '\n');
if (*wszFirstHeader == '\n') { *pbstrHeaders = SysAllocString(wszFirstHeader + 1);
if (!*pbstrHeaders) goto ErrorOutOfMemory; }
hr = NOERROR; Cleanup: if (wszAllHeaders) delete [] wszAllHeaders;
SetErrorInfo(hr); return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
Error: if (pbstrHeaders) { SysFreeString(*pbstrHeaders); *pbstrHeaders = NULL; } goto Cleanup; }
/*
* CHttpRequest::get_status * * Purpose: * Get the request status code * * Parameters: * plStatus OUT HTTP request status code * * Errors: * E_FAIL * E_INVALIDARG * E_UNEXPECTED */
STDMETHODIMP CHttpRequest::get_Status(long * plStatus) { HRESULT hr = NOERROR; DWORD cb = sizeof(DWORD); BOOL fRetCode; DWORD dwStatus;
// Validate parameter
if (IsBadWritePtr(plStatus, sizeof(long))) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend;
fRetCode = HttpQueryInfoA( _hHTTP, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &dwStatus, &cb, 0);
if (!fRetCode) goto ErrorFail;
*plStatus = dwStatus;
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
Error: goto Cleanup; }
/*
* CHttpRequest::get_StatusText * * Purpose: * Get the request status text * * Parameters: * pbstrStatus OUT HTTP request status text * * Errors: * E_FAIL * E_INVALIDARG * E_UNEXPECTED */
STDMETHODIMP CHttpRequest::get_StatusText(BSTR * pbstrStatus) { HRESULT hr = NOERROR; WCHAR wszStatusText[32]; WCHAR * pwszStatusText = wszStatusText; BOOL fFreeStatusString = FALSE; DWORD cb; BOOL fRetCode;
// Validate parameter
if (IsBadWritePtr(pbstrStatus, sizeof(BSTR))) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend;
*pbstrStatus = NULL;
cb = sizeof(wszStatusText) / sizeof(WCHAR);
RetryQuery: fRetCode = WinHttpQueryHeaders( _hHTTP, HTTP_QUERY_STATUS_TEXT, WINHTTP_HEADER_NAME_BY_INDEX, pwszStatusText, &cb, 0);
if (!fRetCode) { // Check for ERROR_INSUFFICIENT_BUFFER error
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { // Reallocate the status text buffer
if (fFreeStatusString) delete pwszStatusText;
pwszStatusText = New WCHAR[cb];
if (!pwszStatusText) goto ErrorOutOfMemory;
fFreeStatusString = TRUE;
goto RetryQuery; } else { goto ErrorFail; } }
// Convert the status text to a BSTR
*pbstrStatus = SysAllocString(pwszStatusText);
if (!*pbstrStatus) goto ErrorOutOfMemory;
hr = NOERROR;
Cleanup: if (fFreeStatusString) delete [] pwszStatusText; SetErrorInfo(hr); return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup;
Error: goto Cleanup; }
/*
* CHttpRequest::get_ResponseBody * * Purpose: * Retrieve the response body as a SAFEARRAY of UI1 * * Parameters: * pvarBody OUT Response blob * * Errors: * E_INVALIDARG * E_UNEXPECTED * E_PENDING */
STDMETHODIMP CHttpRequest::get_ResponseBody(VARIANT * pvarBody) { HRESULT hr = NOERROR;
// Validate parameter
if (IsBadWritePtr(pvarBody, sizeof(VARIANT))) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend; else if (_eState < CHttpRequest::RESPONSE) goto ErrorPending;
VariantInit(pvarBody);
if (_szResponseBuffer) { hr = CreateVector( pvarBody, (const BYTE *)_szResponseBuffer, _cbResponseBody);
if (FAILED(hr)) goto Error; } else { V_VT(pvarBody) = VT_EMPTY; }
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
ErrorPending: hr = E_PENDING; goto Error;
Error: if (pvarBody) VariantClear(pvarBody); goto Cleanup; }
/*
* CHttpRequest::get_ResponseText * * Purpose: * Retrieve the response body as a BSTR * * Parameters: * pbstrBody OUT response as a BSTR * * Errors: * E_INVALIDARG * E_OUTOFMEMORY * E_UNEXPECTED * E_PENDING */
STDMETHODIMP CHttpRequest::get_ResponseText(BSTR * pbstrBody) { HRESULT hr;
// Validate parameter
if (IsBadWritePtr(pbstrBody, sizeof(BSTR))) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend; else if (_eState < CHttpRequest::RESPONSE) goto ErrorPending;
hr = AsciiToBSTR(pbstrBody, _szResponseBuffer, (int)_cbResponseBody); if (FAILED(hr)) goto Error;
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
ErrorPending: hr = E_PENDING; goto Error;
Error: goto Cleanup; }
STDMETHODIMP CHttpRequest::get_ResponseStream(VARIANT * pvarBody) { HRESULT hr = NOERROR; IStream * pStm = NULL;
// Validate parameter
if (IsBadWritePtr(pvarBody, sizeof(VARIANT))) return E_INVALIDARG;
// Validate state
if (_eState < CHttpRequest::SENDING) goto ErrorCannotCallBeforeSend; else if (_eState < CHttpRequest::RESPONSE) goto ErrorPending;
VariantInit(pvarBody);
hr = CreateStreamOnResponse(&pStm); if (FAILED(hr)) goto Error;
V_VT(pvarBody) = VT_UNKNOWN; pvarBody->punkVal = pStm;
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeSend: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND); goto Error;
ErrorPending: hr = E_PENDING; goto Error;
Error: if (pvarBody) VariantClear(pvarBody); goto Cleanup; }
void CHttpRequest::SetState(State state) { if (_fAsync) { InterlockedExchange((long *)&_eState, state); } else { _eState = state; } }
/*
* CHttpRequest::CreateStreamOnResponse * * Purpose: * Create a Stream containing the Response data * * Parameters: * ppStm IN/OUT Stream * * Errors: * E_INVALIDARG * E_OUTOFMEMORY */
HRESULT CHttpRequest::CreateStreamOnResponse(IStream ** ppStm) { HRESULT hr = NOERROR; ULONG cbWritten; LARGE_INTEGER liReset = { 0 };
if (!ppStm) return E_INVALIDARG;
*ppStm = NULL;
hr = CreateStreamOnHGlobal(NULL, TRUE, ppStm);
if (FAILED(hr)) goto ErrorOutOfMemory;
hr = (*ppStm)->Write(_szResponseBuffer, _cbResponseBody, &cbWritten);
if (FAILED(hr)) goto Error;
hr = (*ppStm)->Seek(liReset, STREAM_SEEK_SET, NULL);
if (FAILED(hr)) goto Error;
hr = NOERROR;
Cleanup: return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
Error: if (ppStm) SafeRelease(*ppStm); goto Cleanup; }
STDMETHODIMP CHttpRequest::get_Option(WinHttpRequestOption Option, VARIANT * Value) { HRESULT hr;
if (IsBadWritePtr(Value, sizeof(VARIANT))) return E_INVALIDARG;
switch (Option) { case WinHttpRequestOption_UserAgentString: { V_BSTR(Value) = SysAllocString(GetUserAgentString());
if (V_BSTR(Value) == NULL) goto ErrorOutOfMemory;
V_VT(Value) = VT_BSTR;
break; }
case WinHttpRequestOption_URL: { WCHAR * pwszUrl = NULL; DWORD dwBufferSize = 0;
if (_eState < CHttpRequest::OPENED) goto ErrorCannotCallBeforeOpen;
if (!WinHttpQueryOption(_hHTTP, WINHTTP_OPTION_URL, NULL, &dwBufferSize) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { pwszUrl = New WCHAR[dwBufferSize + 1];
if (!pwszUrl) goto ErrorOutOfMemory;
if (WinHttpQueryOption(_hHTTP, WINHTTP_OPTION_URL, pwszUrl, &dwBufferSize)) { V_BSTR(Value) = SysAllocString(pwszUrl); V_VT(Value) = VT_BSTR;
hr = NOERROR; } else { hr = E_FAIL; }
delete [] pwszUrl;
if (FAILED(hr)) { goto ErrorFail; } else if (V_BSTR(Value) == NULL) { goto ErrorOutOfMemory; } } break; }
case WinHttpRequestOption_URLCodePage: V_I4(Value) = (long) _dwCodePage; V_VT(Value) = VT_I4; break; case WinHttpRequestOption_EscapePercentInURL: V_BOOL(Value) = (_dwEscapePercentFlag==WINHTTP_FLAG_ESCAPE_PERCENT) ? VARIANT_TRUE : VARIANT_FALSE; V_VT(Value) = VT_BOOL; break;
case WinHttpRequestOption_EnableRedirects: V_BOOL(Value) = _bDisableRedirects ? VARIANT_FALSE : VARIANT_TRUE; V_VT(Value) = VT_BOOL; break;
default: VariantInit(Value); return E_INVALIDARG; }
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorCannotCallBeforeOpen: hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN); goto Error;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
Error: goto Cleanup; }
STDMETHODIMP CHttpRequest::put_Option(WinHttpRequestOption Option, VARIANT Value) { HRESULT hr;
if (!IsValidVariant(Value)) return E_INVALIDARG;
switch (Option) { case WinHttpRequestOption_UserAgentString: { BSTR bstrUserAgent = GetBSTRFromVariant(Value);
if (!bstrUserAgent || (*bstrUserAgent == L'\0')) return E_INVALIDARG;
if (_hInet) { if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_USER_AGENT, bstrUserAgent, SysStringLen(bstrUserAgent))) { goto ErrorFail; } }
if (_hHTTP) { if (!WinHttpSetOption(_hHTTP, WINHTTP_OPTION_USER_AGENT, bstrUserAgent, SysStringLen(bstrUserAgent))) { goto ErrorFail; } }
if (_bstrUserAgent) SysFreeString(_bstrUserAgent); _bstrUserAgent = bstrUserAgent; break; }
case WinHttpRequestOption_URL: // The URL cannot be set using the Option property.
return E_INVALIDARG;
case WinHttpRequestOption_URLCodePage: { _dwCodePage = GetDwordFromVariant(Value, CP_UTF8);
if (_hInet) { if (!WinHttpSetOption(_hInet, WINHTTP_OPTION_CODEPAGE, &_dwCodePage, sizeof(_dwCodePage))) goto ErrorFail; }
if (_hConnection) { if (!WinHttpSetOption(_hConnection, WINHTTP_OPTION_CODEPAGE, &_dwCodePage, sizeof(_dwCodePage))) goto ErrorFail; }
break; } case WinHttpRequestOption_EscapePercentInURL: { BOOL fEscapePercent = GetBoolFromVariant(Value, FALSE);
_dwEscapePercentFlag = (fEscapePercent ? WINHTTP_FLAG_ESCAPE_PERCENT : 0); break; }
case WinHttpRequestOption_SslErrorIgnoreFlags: { DWORD dwSslIgnoreFlags = GetDwordFromVariant(Value, _dwSslIgnoreFlags); if (dwSslIgnoreFlags & ~SECURITY_INTERNET_MASK) { return E_INVALIDARG; }
// Break automatically, so we don't need the overhead of a callback.
dwSslIgnoreFlags |= SECURITY_FLAG_BREAK_ON_STATUS_SECURE_FAILURE;
if (_hHTTP) { // Set the SSL ignore flags through an undocumented front door
if (!WinHttpSetOption(_hHTTP, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&dwSslIgnoreFlags, sizeof(dwSslIgnoreFlags))) goto ErrorFail; } _dwSslIgnoreFlags = dwSslIgnoreFlags; break; }
case WinHttpRequestOption_SelectCertificate: { BSTR bstrCertSubject = GetBSTRFromVariant(Value);
if (!bstrCertSubject) { // Allow all empty calls to be interpreted as "first enum"
bstrCertSubject = SysAllocString(L""); if (!bstrCertSubject) return E_OUTOFMEMORY; } if (_bstrCertSubject) SysFreeString(_bstrCertSubject); _bstrCertSubject = bstrCertSubject; break; }
case WinHttpRequestOption_EnableRedirects: { BOOL fEnableRedirects = GetBoolFromVariant(Value, TRUE);
_bDisableRedirects = fEnableRedirects ? false : true; break; }
default: return E_INVALIDARG; }
hr = NOERROR;
Cleanup: SetErrorInfo(hr); return hr;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Error;
Error: goto Cleanup; }
void CHttpRequest::SetErrorInfo(HRESULT hr) { if (SUCCEEDED(hr)) return; ICreateErrorInfo * pCErrInfo = NULL; IErrorInfo * pErrInfo = NULL; DWORD error = hr; DWORD dwFmtMsgFlag = FORMAT_MESSAGE_FROM_SYSTEM; HMODULE hModule = NULL; DWORD rc; LPWSTR pwszMessage = NULL; const DWORD dwSize = 512;
pwszMessage = New WCHAR[dwSize]; if (pwszMessage == NULL) return; if (HRESULT_FACILITY(hr) == FACILITY_WIN32) { DWORD errcode = HRESULT_CODE(hr);
if ((errcode > WINHTTP_ERROR_BASE) && (errcode <= WINHTTP_ERROR_LAST)) { dwFmtMsgFlag = FORMAT_MESSAGE_FROM_HMODULE;
hModule = GetModuleHandle("WINHTTP5.DLL");
error = errcode; } }
rc = ::FormatMessageW(dwFmtMsgFlag, hModule, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
pwszMessage, dwSize, NULL);
if (rc != 0) { if (SUCCEEDED(::CreateErrorInfo(&pCErrInfo))) { if (SUCCEEDED(pCErrInfo->QueryInterface(IID_IErrorInfo, (void **) &pErrInfo))) { pCErrInfo->SetSource(L"WinHttp.WinHttpRequest"); pCErrInfo->SetGUID(IID_IWinHttpRequest);
pCErrInfo->SetDescription(pwszMessage); ::SetErrorInfo(0, pErrInfo);
pErrInfo->Release(); }
pCErrInfo->Release(); } }
delete [] pwszMessage; }
BOOL CHttpRequest::SelectCertificate() { HCERTSTORE hMyStore = NULL; BOOL fRet = FALSE; HANDLE hThreadToken = NULL; PCCERT_CONTEXT pCertContext = NULL; // If impersonating, revert while trying to obtain the cert
if (OpenThreadToken(GetCurrentThread(), (TOKEN_IMPERSONATE | TOKEN_READ), FALSE, &hThreadToken)) { INET_ASSERT(hThreadToken != 0);
RevertToSelf(); }
// For now, just pick the first enum available
// based on a simple CERT_ FIND_SUBJECT_STR criteria
// If the user selected an empty string, then simply
// pick the first enum.
hMyStore = CertOpenSystemStore(NULL, "MY"); if (!hMyStore) { goto Cleanup; }
if (_bstrCertSubject && _bstrCertSubject[0]) { pCertContext = CertFindCertificateInStore(hMyStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, (LPVOID) _bstrCertSubject, NULL); } else { pCertContext = CertFindCertificateInStore(hMyStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); }
// Winhttp5 will add a couple of new options.
// One will be an option to request that client auth
// certs are not cached, so they can be session based.
// Another will be for flushing the SSL cache on demand.
//
// For now, set this via a private method off of the
// request object.
if (pCertContext) { fRet = WinHttpSetOption(_hHTTP, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, (LPVOID) pCertContext, sizeof(CERT_CONTEXT)); }
Cleanup: if (pCertContext) CertFreeCertificateContext(pCertContext);
if (hMyStore) CertCloseStore(hMyStore, 0); // Restore the impersonating state for the current thread.
if (hThreadToken) { SetThreadToken(NULL, hThreadToken); CloseHandle(hThreadToken); } return fRet; }
/*
* BSTRToUTF8 * * Purpose: * Convert a BSTR to UTF-8 * */
static HRESULT BSTRToUTF8(char ** psz, DWORD * pcbUTF8, BSTR bstr) { UINT cch = lstrlenW(bstr); *pcbUTF8 = 0; *psz = NULL; if (cch == 0) return NOERROR;
// Allocate the maximum buffer the UTF-8 string could be
*psz = New char [(cch * 3) + 1]; if (!*psz) return E_OUTOFMEMORY;
*pcbUTF8 = cch * 3;
WideCharToUtf8(bstr, cch, (BYTE *)*psz, (UINT *)pcbUTF8);
(*psz)[*pcbUTF8] = 0;
return NOERROR; }
/**
* Scans buffer and translates Unicode characters into UTF8 characters */ static void WideCharToUtf8( WCHAR * buffer, UINT cch, BYTE * bytebuffer, UINT * cb) { UINT count = 0, m1 = *cb, m2 = m1 - 1, m3 = m2 - 1, m4 = m3 - 1; DWORD dw1; bool surrogate = false;
for (UINT i = cch; i > 0; i--) { DWORD dw = *buffer;
if (surrogate) // is it the second char of a surrogate pair?
{ if (dw >= 0xdc00 && dw <= 0xdfff) { // four bytes 0x11110xxx 0x10xxxxxx 0x10xxxxxx 0x10xxxxxx
if (count < m4) count += 4; else break; ULONG ucs4 = (dw1 - 0xd800) * 0x400 + (dw - 0xdc00) + 0x10000; *bytebuffer++ = (byte)(( ucs4 >> 18) | 0xF0); *bytebuffer++ = (byte)((( ucs4 >> 12) & 0x3F) | 0x80); *bytebuffer++ = (byte)((( ucs4 >> 6) & 0x3F) | 0x80); *bytebuffer++ = (byte)(( ucs4 & 0x3F) | 0x80); surrogate = false; buffer++; continue; } else // Then dw1 must be a three byte character
{ if (count < m3) count += 3; else break; *bytebuffer++ = (byte)(( dw1 >> 12) | 0xE0); *bytebuffer++ = (byte)((( dw1 >> 6) & 0x3F) | 0x80); *bytebuffer++ = (byte)(( dw1 & 0x3F) | 0x80); } surrogate = false; }
if (dw < 0x80) // one byte, 0xxxxxxx
{ if (count < m1) count++; else break; *bytebuffer++ = (byte)dw; } else if ( dw < 0x800) // two WORDS, 110xxxxx 10xxxxxx
{ if (count < m2) count += 2; else break; *bytebuffer++ = (byte)((dw >> 6) | 0xC0); *bytebuffer++ = (byte)((dw & 0x3F) | 0x80); } else if (dw >= 0xd800 && dw <= 0xdbff) // Assume that it is the first char of surrogate pair
{ if (i == 1) // last wchar in buffer
break; dw1 = dw; surrogate = true; } else // three bytes, 1110xxxx 10xxxxxx 10xxxxxx
{ if (count < m3) count += 3; else break; *bytebuffer++ = (byte)(( dw >> 12) | 0xE0); *bytebuffer++ = (byte)((( dw >> 6) & 0x3F) | 0x80); *bytebuffer++ = (byte)(( dw & 0x3F) | 0x80); } buffer++; }
*cb = count; }
/*
* AsciiToBSTR * * Purpose: * Convert an ascii string to a BSTR * */
static HRESULT AsciiToBSTR(BSTR * pbstr, char * sz, int cch) { int cwch;
// Determine how big the ascii string will be
cwch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, cch, NULL, 0);
*pbstr = SysAllocStringLen(NULL, cwch);
if (!*pbstr) return E_OUTOFMEMORY;
cch = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, cch, *pbstr, cwch);
return NOERROR; }
/*
* BSTRToAscii * * Purpose: * Convert a BSTR to an ascii string * */
static HRESULT BSTRToAscii(char ** psz, BSTR bstr) { int cch;
*psz = NULL;
if (!bstr) return S_OK;
// Determine how big the ascii string will be
cch = WideCharToMultiByte(CP_ACP, 0, bstr, SysStringLen(bstr), NULL, 0, NULL, NULL);
*psz = New char[cch + 1];
if (!*psz) return E_OUTOFMEMORY;
// Determine how big the ascii string will be
cch = WideCharToMultiByte(CP_ACP, 0, bstr, SysStringLen(bstr), *psz, cch, NULL, NULL);
(*psz)[cch] = 0; return S_OK; }
/*
* GetBSTRFromVariant * * Purpose: * Convert a VARIANT to a BSTR * */
static BSTR GetBSTRFromVariant(VARIANT varVariant) { VARIANT varTemp; HRESULT hr; BSTR bstrResult = NULL;
if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL && V_VT(&varVariant) != VT_ERROR) { VariantInit(&varTemp);
hr = VariantChangeType( &varTemp, &varVariant, 0, VT_BSTR);
if (FAILED(hr)) goto Error; bstrResult = V_BSTR(&varTemp); // take over ownership of BSTR
}
hr = NOERROR;
Cleanup: // DON'T clear the variant because we stole the BSTR
//VariantClear(&varTemp);
return bstrResult;
Error: goto Cleanup; }
/*
* GetBoolFromVariant * * Purpose: * Convert a VARIANT to a Boolean * */
static BOOL GetBoolFromVariant(VARIANT varVariant, BOOL fDefault) { HRESULT hr; BOOL fResult = fDefault;
if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL && V_VT(&varVariant) != VT_ERROR) { VARIANT varTemp; VariantInit(&varTemp); hr = VariantChangeType( &varTemp, &varVariant, 0, VT_BOOL); if (FAILED(hr)) goto Cleanup; fResult = V_BOOL(&varTemp) == VARIANT_TRUE ? TRUE : FALSE; }
hr = NOERROR;
Cleanup: return fResult; }
/*
* GetDwordFromVariant * * Purpose: * Convert a VARIANT to a DWORD * */
static DWORD GetDwordFromVariant(VARIANT varVariant, DWORD dwDefault) { HRESULT hr; DWORD dwResult = dwDefault;
if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL && V_VT(&varVariant) != VT_ERROR) { VARIANT varTemp; VariantInit(&varTemp); hr = VariantChangeType( &varTemp, &varVariant, 0, VT_UI4); if (FAILED(hr)) goto Cleanup; dwResult = V_UI4(&varTemp); }
hr = NOERROR;
Cleanup: return dwResult; }
/*
* GetDwordFromVariant * * Purpose: * Convert a VARIANT to a DWORD * */
static long GetLongFromVariant(VARIANT varVariant, long lDefault) { HRESULT hr; long lResult = lDefault;
if (V_VT(&varVariant) != VT_EMPTY && V_VT(&varVariant) != VT_NULL && V_VT(&varVariant) != VT_ERROR) { VARIANT varTemp; VariantInit(&varTemp); hr = VariantChangeType( &varTemp, &varVariant, 0, VT_I4); if (FAILED(hr)) goto Cleanup; lResult = V_I4(&varTemp); }
hr = NOERROR;
Cleanup: return lResult; }
/**
* Helper to create a char safearray from a string */ static HRESULT CreateVector(VARIANT * pVar, const BYTE * pData, DWORD cElems) { HRESULT hr; BYTE * pB;
SAFEARRAY * psa = SafeArrayCreateVector(VT_UI1, 0, cElems); if (!psa) { hr = E_OUTOFMEMORY; goto Cleanup; }
hr = SafeArrayAccessData(psa, (void **)&pB); if (hr) goto Error;
memcpy(pB, pData, cElems);
SafeArrayUnaccessData(psa); INET_ASSERT((pVar->vt == VT_EMPTY) || (pVar->vt == VT_NULL)); V_ARRAY(pVar) = psa; pVar->vt = VT_ARRAY | VT_UI1;
hr = NOERROR;
Cleanup: return hr;
Error: if (psa) SafeArrayDestroy(psa); goto Cleanup; }
/*
* ReadFromStream * * Purpose: * Extract the contents of a stream into a buffer. * * Parameters: * ppBuf IN/OUT Buffer * pStm IN Stream * * Errors: * E_INVALIDARG * E_OUTOFMEMORY */
static HRESULT ReadFromStream(char ** ppData, ULONG * pcbData, IStream * pStm) { HRESULT hr = NOERROR; char * pBuffer = NULL; // Buffer
ULONG cbBuffer = 0; // Bytes in buffer
ULONG cbData = 0; // Bytes of data in buffer
ULONG cbRead = 0; // Bytes read from stream
ULONG cbNewSize = 0; char * pNewBuf = NULL;
if (!ppData || !pStm) return E_INVALIDARG;
*ppData = NULL; *pcbData = 0; while (TRUE) { if (cbData + 512 > cbBuffer) { cbNewSize = (cbData ? cbData*2 : 4096); pNewBuf = New char[cbNewSize+1];
if (!pNewBuf) goto ErrorOutOfMemory;
if (cbData) ::memcpy(pNewBuf, pBuffer, cbData);
cbBuffer = cbNewSize; delete[] pBuffer; pBuffer = pNewBuf; pBuffer[cbData] = 0; }
hr = pStm->Read( &pBuffer[cbData], cbBuffer - cbData, &cbRead);
if (FAILED(hr)) goto Error; cbData += cbRead; pBuffer[cbData] = 0;
// No more data
if (cbRead == 0) break; }
*ppData = pBuffer; *pcbData = cbData;
hr = NOERROR;
Cleanup: return hr;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Error;
Error: if (pBuffer) delete [] pBuffer; goto Cleanup; }
static void MessageLoop() { MSG msg;
// There is a window message available. Dispatch it.
while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
static DWORD UpdateTimeout(DWORD dwTimeout, DWORD dwStartTime) { if (dwTimeout != INFINITE) { DWORD dwTimeNow = GetTickCount(); DWORD dwElapsedTime;
if (dwTimeNow >= dwStartTime) { dwElapsedTime = dwTimeNow - dwStartTime; } else { dwElapsedTime = dwTimeNow + (0xFFFFFFFF - dwStartTime); }
if (dwElapsedTime < dwTimeout) { dwTimeout -= dwElapsedTime; } else { dwTimeout = 0; } }
return dwTimeout; }
DWORD CSinkArray::Add(IUnknown * pUnk) { ULONG iIndex;
IUnknown** pp = NULL;
if (_nSize == 0) // no connections
{ _pUnk = pUnk; _nSize = 1; return 1; } else if (_nSize == 1) { // create array
pp = (IUnknown **)ALLOCATE_FIXED_MEMORY(sizeof(IUnknown*)* _DEFAULT_VECTORLENGTH); if (pp == NULL) return 0; memset(pp, 0, sizeof(IUnknown *) * _DEFAULT_VECTORLENGTH); *pp = _pUnk; _ppUnk = pp; _nSize = _DEFAULT_VECTORLENGTH; }
for (pp = begin(); pp < end(); pp++) { if (*pp == NULL) { *pp = pUnk; iIndex = ULONG(pp-begin()); return iIndex+1; } }
int nAlloc = _nSize*2; pp = (IUnknown **)REALLOCATE_MEMORY(_ppUnk, sizeof(IUnknown*)*nAlloc, LMEM_ZEROINIT); if (pp == NULL) return 0;
_ppUnk = pp; memset(&_ppUnk[_nSize], 0, sizeof(IUnknown *)*_nSize); _ppUnk[_nSize] = pUnk; iIndex = _nSize; _nSize = nAlloc; return iIndex+1; }
BOOL CSinkArray::Remove(DWORD dwCookie) { ULONG iIndex;
if (dwCookie == NULL) return FALSE; if (_nSize == 0) return FALSE; iIndex = dwCookie-1; if (iIndex >= (ULONG)_nSize) return FALSE; if (_nSize == 1) { _nSize = 0; return TRUE; } begin()[iIndex] = NULL; return TRUE; }
void CSinkArray::ReleaseAll() { for (IUnknown ** pp = begin(); pp < end(); pp++) { if (*pp != NULL) { SafeRelease(*pp); } } }
HRESULT STDMETHODCALLTYPE CSinkArray::QueryInterface(REFIID, void **) { return E_NOTIMPL; }
ULONG STDMETHODCALLTYPE CSinkArray::AddRef() { return 2; }
ULONG STDMETHODCALLTYPE CSinkArray::Release() { return 1; }
void STDMETHODCALLTYPE CSinkArray::OnResponseStart(long Status, BSTR bstrContentType) { for (IUnknown ** pp = begin(); pp < end(); pp++) { if (*pp != NULL) { IWinHttpRequestEvents * pSink;
pSink = static_cast<IWinHttpRequestEvents *>(*pp);
if (((*(DWORD_PTR **)pSink)[3]) != NULL) { pSink->OnResponseStart(Status, bstrContentType); } } } }
void STDMETHODCALLTYPE CSinkArray::OnResponseDataAvailable(SAFEARRAY ** ppsaData) { for (IUnknown ** pp = begin(); pp < end(); pp++) { if (*pp != NULL) { IWinHttpRequestEvents * pSink;
pSink = static_cast<IWinHttpRequestEvents *>(*pp);
if (((*(DWORD_PTR **)pSink)[4]) != NULL) { pSink->OnResponseDataAvailable(ppsaData); } } } }
void STDMETHODCALLTYPE CSinkArray::OnResponseFinished(void) { for (IUnknown ** pp = begin(); pp < end(); pp++) { if (*pp != NULL) { IWinHttpRequestEvents * pSink;
pSink = static_cast<IWinHttpRequestEvents *>(*pp);
if (((*(DWORD_PTR **)pSink)[5]) != NULL) { pSink->OnResponseFinished(); } } } }
CWinHttpRequestEventsMarshaller::CWinHttpRequestEventsMarshaller ( CSinkArray * pSinkArray, HWND hWnd ) { INET_ASSERT((pSinkArray != NULL) && (hWnd != NULL));
_pSinkArray = pSinkArray; _hWnd = hWnd; _cRefs = 0; _bFireEvents = true;
_cs.Init(); }
CWinHttpRequestEventsMarshaller::~CWinHttpRequestEventsMarshaller() { INET_ASSERT(_pSinkArray == NULL); INET_ASSERT(_hWnd == NULL); INET_ASSERT(_cRefs == 0); }
HRESULT CWinHttpRequestEventsMarshaller::Create ( CSinkArray * pSinkArray, CWinHttpRequestEventsMarshaller ** ppSinkMarshaller ) { CWinHttpRequestEventsMarshaller * pSinkMarshaller = NULL; HWND hWnd = NULL; HRESULT hr = NOERROR;
if (!RegisterWinHttpEventMarshallerWndClass()) goto ErrorFail;
hWnd = CreateWindowEx(0, s_szWinHttpEventMarshallerWndClass, NULL, 0, 0, 0, 0, 0, (IsPlatformWinNT() && GlobalPlatformVersion5) ? HWND_MESSAGE : NULL, NULL, GlobalDllHandle, NULL);
if (!hWnd) goto ErrorFail;
pSinkMarshaller = New CWinHttpRequestEventsMarshaller(pSinkArray, hWnd);
if (!pSinkMarshaller) goto ErrorOutOfMemory;
SetLastError(0); SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) pSinkMarshaller);
if (GetLastError() != 0) goto ErrorFail;
pSinkMarshaller->AddRef();
*ppSinkMarshaller = pSinkMarshaller;
Exit: if (FAILED(hr)) { if (pSinkMarshaller) { delete pSinkMarshaller; } else if (hWnd) { DestroyWindow(hWnd); } }
return hr;
ErrorFail: hr = HRESULT_FROM_WIN32(GetLastError()); goto Exit;
ErrorOutOfMemory: hr = E_OUTOFMEMORY; goto Exit; }
void CWinHttpRequestEventsMarshaller::Shutdown() { if (_cs.Lock()) { FreezeEvents();
if (_hWnd) { MessageLoop();
DestroyWindow(_hWnd); _hWnd = NULL; }
_pSinkArray = NULL;
_cs.Unlock(); } }
LRESULT CALLBACK CWinHttpRequestEventsMarshaller::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { if (msg >= WHREM_MSG_ON_RESPONSE_START && msg <= WHREM_MSG_ON_RESPONSE_FINISHED) { CWinHttpRequestEventsMarshaller * pMarshaller; CSinkArray * pSinkArray; bool bOkToFireEvents = false;
pMarshaller = (CWinHttpRequestEventsMarshaller *) GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (pMarshaller) { pSinkArray = pMarshaller->GetSinkArray(); bOkToFireEvents = pMarshaller->OkToFireEvents(); }
switch (msg) { case WHREM_MSG_ON_RESPONSE_START: { BSTR bstrContentType = (BSTR) lParam;
if (bOkToFireEvents) { pSinkArray->OnResponseStart((long) wParam, bstrContentType); }
if (bstrContentType) { SysFreeString(bstrContentType); } } break;
case WHREM_MSG_ON_RESPONSE_DATA_AVAILABLE: { SAFEARRAY * psaData = (SAFEARRAY *) wParam;
if (bOkToFireEvents) { pSinkArray->OnResponseDataAvailable(&psaData); }
if (psaData) { SafeArrayDestroy(psaData); } } break;
case WHREM_MSG_ON_RESPONSE_FINISHED: if (bOkToFireEvents) { pSinkArray->OnResponseFinished(); } break; }
return 0; } else { return DefWindowProc(hWnd, msg, wParam, lParam); } }
HRESULT STDMETHODCALLTYPE CWinHttpRequestEventsMarshaller::QueryInterface(REFIID riid, void ** ppv) { HRESULT hr = NOERROR;
if (ppv == NULL) { hr = E_INVALIDARG; } else if (riid == IID_IWinHttpRequestEvents || riid == IID_IUnknown) { *ppv = static_cast<IWinHttpRequestEvents *>(this); AddRef(); } else hr = E_NOINTERFACE;
return hr; }
ULONG STDMETHODCALLTYPE CWinHttpRequestEventsMarshaller::AddRef() { return InterlockedIncrement(&_cRefs); }
ULONG STDMETHODCALLTYPE CWinHttpRequestEventsMarshaller::Release() { DWORD cRefs = InterlockedDecrement(&_cRefs);
if (cRefs == 0) { delete this; return 0; } else return cRefs; }
void STDMETHODCALLTYPE CWinHttpRequestEventsMarshaller::OnResponseStart(long Status, BSTR bstrContentType) { if (_cs.Lock()) { if (OkToFireEvents()) { BSTR bstrContentTypeCopy;
bstrContentTypeCopy = SysAllocString(bstrContentType);
PostMessage(_hWnd, WHREM_MSG_ON_RESPONSE_START, (WPARAM) Status, (LPARAM) bstrContentTypeCopy);
// Note: ownership of bstrContentType is transferred to the
// message window, so the string is not freed here.
} _cs.Unlock(); } }
void STDMETHODCALLTYPE CWinHttpRequestEventsMarshaller::OnResponseDataAvailable(SAFEARRAY ** ppsaData) {
if (_cs.Lock()) { if (OkToFireEvents()) { SAFEARRAY * psaDataCopy = NULL;
if (SUCCEEDED(SafeArrayCopy(*ppsaData, &psaDataCopy))) { PostMessage(_hWnd, WHREM_MSG_ON_RESPONSE_DATA_AVAILABLE, (WPARAM) psaDataCopy, 0); }
// Note: ownership of psaDataCopy is transferred to the
// message window, so the array is not freed here.
} _cs.Unlock(); } }
void STDMETHODCALLTYPE CWinHttpRequestEventsMarshaller::OnResponseFinished(void) { if (_cs.Lock()) { if (OkToFireEvents()) { PostMessage(_hWnd, WHREM_MSG_ON_RESPONSE_FINISHED, 0, 0); }
_cs.Unlock(); } }
BOOL RegisterWinHttpEventMarshallerWndClass() { if (s_fWndClassRegistered) return TRUE;
// only one thread should be here
if (!GeneralInitCritSec.Lock()) return FALSE;
if (s_fWndClassRegistered == FALSE) { WNDCLASS wndclass;
wndclass.style = 0; wndclass.lpfnWndProc = &CWinHttpRequestEventsMarshaller::WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = GlobalDllHandle; wndclass.hIcon = NULL; wndclass.hCursor = NULL;; wndclass.hbrBackground = (HBRUSH)NULL; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = s_szWinHttpEventMarshallerWndClass;
// Register the window class
if (RegisterClass(&wndclass)) { s_fWndClassRegistered = TRUE; } }
GeneralInitCritSec.Unlock();
return s_fWndClassRegistered; }
void CleanupWinHttpRequestGlobals() { if (s_fWndClassRegistered) { // Register the window class
if (UnregisterClass(s_szWinHttpEventMarshallerWndClass, GlobalDllHandle)) { s_fWndClassRegistered = FALSE; } } }
static BOOL IsValidVariant(VARIANT v) { BOOL fOk = TRUE;
if (V_ISBYREF(&v)) { if (IsBadReadPtr(v.pvarVal, sizeof(VARIANT))) { fOk = FALSE; goto Exit; } else v = *(v.pvarVal); }
switch (v.vt) { case VT_BSTR: fOk = IsValidBstr(v.bstrVal); break;
case (VT_BYREF | VT_BSTR): fOk = !IsBadReadPtr(v.pbstrVal, sizeof(BSTR)); break;
case (VT_BYREF | VT_VARIANT): fOk = !IsBadReadPtr(v.pvarVal, sizeof(VARIANT)) && IsValidVariant(*(v.pvarVal)); break;
case VT_UNKNOWN: case VT_DISPATCH: fOk = !IsBadReadPtr(v.punkVal, sizeof(void *)); break; }
Exit: return fOk; }
|