added winrt networking files

This commit is contained in:
Dale Stammen 2015-05-28 13:48:13 -07:00
parent 2aec149316
commit 60f672599b
3 changed files with 1264 additions and 0 deletions

View File

@ -0,0 +1,366 @@
/****************************************************************************
Copyright (c) 2012 greathqy
Copyright (c) 2012 cocos2d-x.org
Copyright (c) 2013-2014 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "HttpClient.h"
#include <thread>
#include <queue>
#include <condition_variable>
#include <errno.h>
#include "base/CCVector.h"
#include "base/CCDirector.h"
#include "base/CCScheduler.h"
#include "platform/CCFileUtils.h"
#include "HttpConnection-winrt.h"
NS_CC_BEGIN
namespace network {
static std::mutex s_requestQueueMutex;
static std::mutex s_responseQueueMutex;
static std::condition_variable_any s_SleepCondition;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
typedef int int32_t;
#endif
static Vector<HttpRequest*>* s_requestQueue = nullptr;
static Vector<HttpResponse*>* s_responseQueue = nullptr;
static HttpClient *s_pHttpClient = nullptr; // pointer to singleton
static std::string s_errorBuffer ="";
static std::string s_cookieFilename = "";
static std::string s_sslCaFilename = "";
// function used to collect response/header data
static void writeData(std::vector<char> *pFromBuffer, std::vector<char> *pToBuffer)
{
if(nullptr == pFromBuffer || nullptr == pToBuffer)
return;
pToBuffer->insert(pToBuffer->end(), pFromBuffer->begin(), pFromBuffer->end());
}
static void processHttpResponse(HttpResponse* response, std::string& errorStr);
static HttpRequest *s_requestSentinel = new HttpRequest;
// Worker thread
void HttpClient::networkThread()
{
auto scheduler = Director::getInstance()->getScheduler();
while (true)
{
HttpRequest *request;
// step 1: send http request if the requestQueue isn't empty
{
std::lock_guard<std::mutex> lock(s_requestQueueMutex);
while (s_requestQueue->empty()) {
s_SleepCondition.wait(s_requestQueueMutex);
}
request = s_requestQueue->at(0);
s_requestQueue->erase(0);
}
if (request == s_requestSentinel) {
break;
}
// step 2: libcurl sync access
// Create a HttpResponse object, the default setting is http access failed
HttpResponse *response = new (std::nothrow) HttpResponse(request);
processHttpResponse(response, s_errorBuffer);
// add response packet into queue
s_responseQueueMutex.lock();
s_responseQueue->pushBack(response);
s_responseQueueMutex.unlock();
if (nullptr != s_pHttpClient) {
scheduler->performFunctionInCocosThread(CC_CALLBACK_0(HttpClient::dispatchResponseCallbacks, this));
}
}
// cleanup: if worker thread received quit signal, clean up un-completed request queue
s_requestQueueMutex.lock();
s_requestQueue->clear();
s_requestQueueMutex.unlock();
if (s_requestQueue != nullptr) {
delete s_requestQueue;
s_requestQueue = nullptr;
delete s_responseQueue;
s_responseQueue = nullptr;
}
}
// Worker thread
void HttpClient::networkThreadAlone(HttpRequest* request, HttpResponse* response)
{
std::string errorStr;
processHttpResponse(response, errorStr);
auto scheduler = Director::getInstance()->getScheduler();
scheduler->performFunctionInCocosThread([response, request]{
const ccHttpRequestCallback& callback = request->getCallback();
Ref* pTarget = request->getTarget();
SEL_HttpResponse pSelector = request->getSelector();
if (callback != nullptr)
{
callback(s_pHttpClient, response);
}
else if (pTarget && pSelector)
{
(pTarget->*pSelector)(s_pHttpClient, response);
}
response->release();
// do not release in other thread
request->release();
});
}
// Process Response
static void processHttpResponse(HttpResponse* response, std::string& errorStr)
{
auto request = response->getHttpRequest();
long responseCode = -1;
int retValue = 0;
HttpConnection xhr;
bool ok = false;
bool manualAuthReqd = false;
// Process the request -> get response packet
switch (request->getRequestType())
{
case HttpRequest::Type::GET: // HTTP GET
ok = (xhr.init(request) && xhr.open("GET", manualAuthReqd, s_cookieFilename) && xhr.send());
break;
case HttpRequest::Type::POST: // HTTP POST
ok = (xhr.init(request) && xhr.open("POST", manualAuthReqd, s_cookieFilename) && xhr.send());
break;
case HttpRequest::Type::PUT: // HTTP PUT
ok = (xhr.init(request) && xhr.open("PUT", manualAuthReqd, s_cookieFilename) && xhr.send());
break;
case HttpRequest::Type::DELETE: // HTTP DELETE
ok = (xhr.init(request) && xhr.open("DELETE", manualAuthReqd, s_cookieFilename) && xhr.send());
break;
default:
CCASSERT(true, "CCHttpClient: unknown request type, only GET and POST are supported");
break;
}
writeData(xhr.getResponseHeader(), response->getResponseHeader());
writeData(xhr.getResponseData(), response->getResponseData());
retValue = ok ? 0 : 1;
errorStr = xhr.getErrorMessage();
responseCode = xhr.getStatusCode();
// write data to HttpResponse
response->setResponseCode(responseCode);
if (retValue != 0)
{
response->setSucceed(false);
response->setErrorBuffer(errorStr.c_str());
}
else
{
response->setSucceed(true);
}
}
// HttpClient implementation
HttpClient* HttpClient::getInstance()
{
if (s_pHttpClient == nullptr) {
s_pHttpClient = new (std::nothrow) HttpClient();
}
return s_pHttpClient;
}
void HttpClient::destroyInstance()
{
CC_SAFE_DELETE(s_pHttpClient);
}
void HttpClient::enableCookies(const char* cookieFile) {
if (cookieFile) {
s_cookieFilename = std::string(cookieFile);
}
else {
s_cookieFilename = (FileUtils::getInstance()->getWritablePath() + "cookieFile.txt");
}
}
void HttpClient::setSSLVerification(const std::string& caFile)
{
s_sslCaFilename = caFile;
}
HttpClient::HttpClient()
: _timeoutForConnect(30)
, _timeoutForRead(60)
{
}
HttpClient::~HttpClient()
{
if (s_requestQueue != nullptr) {
{
std::lock_guard<std::mutex> lock(s_requestQueueMutex);
s_requestQueue->pushBack(s_requestSentinel);
}
s_SleepCondition.notify_one();
}
s_pHttpClient = nullptr;
}
//Lazy create semaphore & mutex & thread
bool HttpClient::lazyInitThreadSemphore()
{
if (s_requestQueue != nullptr) {
return true;
}
else {
s_requestQueue = new (std::nothrow) Vector<HttpRequest*>();
s_responseQueue = new (std::nothrow) Vector<HttpResponse*>();
auto t = std::thread(CC_CALLBACK_0(HttpClient::networkThread, this));
t.detach();
}
return true;
}
//Add a get task to queue
void HttpClient::send(HttpRequest* request)
{
if (false == lazyInitThreadSemphore())
{
return;
}
if (!request)
{
return;
}
request->retain();
if (nullptr != s_requestQueue) {
s_requestQueueMutex.lock();
s_requestQueue->pushBack(request);
s_requestQueueMutex.unlock();
// Notify thread start to work
s_SleepCondition.notify_one();
}
}
void HttpClient::sendImmediate(HttpRequest* request)
{
if (!request)
{
return;
}
request->retain();
// Create a HttpResponse object, the default setting is http access failed
HttpResponse *response = new (std::nothrow) HttpResponse(request);
auto t = std::thread(&HttpClient::networkThreadAlone, this, request, response);
t.detach();
}
// Poll and notify main thread if responses exists in queue
void HttpClient::dispatchResponseCallbacks()
{
// log("CCHttpClient::dispatchResponseCallbacks is running");
//occurs when cocos thread fires but the network thread has already quited
if (nullptr == s_responseQueue) {
return;
}
HttpResponse* response = nullptr;
s_responseQueueMutex.lock();
if (!s_responseQueue->empty())
{
response = s_responseQueue->at(0);
s_responseQueue->erase(0);
}
s_responseQueueMutex.unlock();
if (response)
{
HttpRequest *request = response->getHttpRequest();
const ccHttpRequestCallback& callback = request->getCallback();
Ref* pTarget = request->getTarget();
SEL_HttpResponse pSelector = request->getSelector();
if (callback != nullptr)
{
callback(this, response);
}
else if (pTarget && pSelector)
{
(pTarget->*pSelector)(this, response);
}
response->release();
// do not release in other thread
request->release();
}
}
}
NS_CC_END

View File

@ -0,0 +1,741 @@
/****************************************************************************
Copyright (c) 2012 greathqy
Copyright (c) 2012 cocos2d-x.org
Copyright (c) 2013-2014 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "network/HttpCookie.h"
#include "HttpConnection-winrt.h"
NS_CC_BEGIN
namespace network {
// Format and add default headers (Platform specific approach)
static void formatHeaders(std::vector<std::string>& headers)
{
#if defined(_XBOX_ONE)
for(auto iter = headers.begin(); iter != headers.end(); ++iter)
{
(*iter) += "\r\n";
}
// append default headers
headers.emplace_back("User-Agent: XB1_IXHR2_HTTP\r\n");
headers.emplace_back("x-xbl-device-type: XboxOne\r\n");
headers.emplace_back("x-xbl-client-type: Console\r\n");
headers.emplace_back("x-xbl-client-version: 1.0\r\n");
headers.emplace_back("x-xbl-contract-version: 1\r\n");
#endif
}
// Get user authentication token (Platform specific approach)
static bool getAuthenticationToken(std::string verb, std::string url, std::string headersXST, std::string bodyXST, std::string& token, std::string& signature)
{
#if defined(_XBOX_ONE)
using namespace Windows::Xbox::System;
token = "";
signature = "";
User^ loggedInUser = nullptr;
int ind = 0;
while(ind < User::Users->Size)
{
loggedInUser = User::Users->GetAt(ind++);
if(loggedInUser->IsSignedIn)
break;
loggedInUser = nullptr;
}
if(nullptr == loggedInUser)
return false;
Platform::Array<unsigned char>^ body;
if(!bodyXST.empty()) {
body = ref new Platform::Array<unsigned char>((unsigned char*)bodyXST.c_str(), bodyXST.size());
}
else {
body = ref new Platform::Array<unsigned char>(1);
body[0] = 0;
}
// this method will crash if TitleId & PrimaryServiceConfigId not specified in Package.appxmanifest.
auto asynOp = loggedInUser->GetTokenAndSignatureAsync(
ref new Platform::String(std::wstring(verb.begin(), verb.end()).c_str()),
ref new Platform::String(std::wstring(url.begin(), url.end()).c_str()),
ref new Platform::String(std::wstring(headersXST.begin(), headersXST.end()).c_str()), body);
bool bRet = false;
HRESULT hr = S_OK;
asynOp->Completed = ref new Windows::Foundation::AsyncOperationCompletedHandler<GetTokenAndSignatureResult^>(
[&token, &signature, &bRet, &hr](Windows::Foundation::IAsyncOperation<GetTokenAndSignatureResult^>^ operation, Windows::Foundation::AsyncStatus status)
{
if(status == Windows::Foundation::AsyncStatus::Completed) {
try
{
auto result = operation->GetResults();
std::wstring tok = result->Token->Data();
std::wstring sig = result->Signature->Data();
token = std::string(tok.begin(), tok.end());
signature = std::string(sig.begin(), sig.end());
bRet = true;
}
catch(Platform::Exception^ e)
{
bRet = false;
}
}
else {
hr = operation->ErrorCode.Value;
if(hr == 0x87dd0021) //AM_E_NO_TOKEN_REQUIRED
bRet = true;
}
});
while(asynOp->Status == Windows::Foundation::AsyncStatus::Started)
{
::Sleep(1);
}
return bRet;
#else
return false;
#endif
}
// CXMLHTTPRequest2Callback
CXHR2Callback::CXHR2Callback() :
_statusCode(0),
_hWfC(nullptr),
_errorMsg("")
{
}
CXHR2Callback::~CXHR2Callback()
{
if (nullptr != _hWfC)
{
CloseHandle(_hWfC);
_hWfC = nullptr;
}
}
HRESULT CXHR2Callback::RuntimeClassInitialize()
{
_hWfC = CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS);
return HRESULT_FROM_WIN32(GetLastError());
}
HRESULT CXHR2Callback::OnRedirect(IXMLHTTPRequest2 *pXHR, const WCHAR *pwszRedirectUrl)
{
UNREFERENCED_PARAMETER(pXHR);
UNREFERENCED_PARAMETER(pwszRedirectUrl);
return S_OK;
}
HRESULT CXHR2Callback::OnHeadersAvailable(IXMLHTTPRequest2 *pXHR, DWORD dwStatus, const WCHAR *pwszStatus)
{
_statusCode = dwStatus;
if(nullptr == pXHR) {
return E_INVALIDARG;
}
WCHAR *headers = nullptr;
HRESULT hr = pXHR->GetAllResponseHeaders(&headers);
if(SUCCEEDED(hr)) {
std::wstring hdr = headers;
_headers.insert(_headers.end(), hdr.begin(), hdr.end());
}
if(headers != nullptr) {
CoTaskMemFree(headers);
headers = nullptr;
}
return hr;
}
HRESULT CXHR2Callback::OnDataAvailable(IXMLHTTPRequest2 *pXHR, ISequentialStream *pResponseStream)
{
UNREFERENCED_PARAMETER(pXHR);
return ReadStreamData(pResponseStream);
}
HRESULT CXHR2Callback::OnResponseReceived(IXMLHTTPRequest2 *pXHR, ISequentialStream *pResponseStream)
{
UNREFERENCED_PARAMETER(pXHR);
HRESULT hr = ReadStreamData(pResponseStream);
CompleteRequest(hr);
return hr;
}
HRESULT CXHR2Callback::OnError(IXMLHTTPRequest2 *pXHR, HRESULT hrError)
{
CompleteRequest(hrError);
return hrError;
}
HRESULT CXHR2Callback::WaitForComplete(PDWORD pdwStatus)
{
HRESULT hr = E_FAIL;
if (NULL != _hWfC)
{
hr = S_OK;
DWORD error;
error = WaitForSingleObjectEx(_hWfC, INFINITE, FALSE);
if (error == WAIT_FAILED) {
hr = HRESULT_FROM_WIN32(GetLastError());
}
if (error != WAIT_OBJECT_0) {
hr = E_ABORT;
}
}
if (SUCCEEDED(hr)) {
*pdwStatus = _statusCode;
}
return hr;
}
HRESULT CXHR2Callback::ReadStreamData(ISequentialStream* pResponseStream)
{
if(pResponseStream == NULL) {
return E_INVALIDARG;
}
CCHAR buff[READ_BUFFER_MAX];
DWORD totalBytes = 0;
DWORD bytesRead = 0;
HRESULT hr = S_OK;
do
{
hr = pResponseStream->Read(buff, READ_BUFFER_MAX, &bytesRead);
if(FAILED(hr)) {
break;
}
_data.insert(_data.end(), &buff[0], buff + bytesRead);
totalBytes += bytesRead;
}
while(hr == S_OK);
if(SUCCEEDED(hr)) {
hr = S_OK;
}
return hr;
}
void CXHR2Callback::CompleteRequest(HRESULT hrError)
{
if (NULL != _hWfC) {
SetEvent(_hWfC);
}
switch (hrError)
{
case S_OK:
case S_FALSE:
_statusCode = 200;
break;
case INET_E_AUTHENTICATION_REQUIRED:
_statusCode = 401;
_errorMsg = ERR_MSG_401;
break;
case INET_E_DOWNLOAD_FAILURE:
_statusCode = 500;
_errorMsg = ERR_MSG_DL_FLD;
break;
case INET_E_FORBIDFRAMING:
_statusCode = 403;
_errorMsg = ERR_MSG_403;
break;
case INET_E_RESOURCE_NOT_FOUND:
_statusCode = 404;
_errorMsg = ERR_MSG_404;
break;
case RPC_S_PROXY_ACCESS_DENIED:
_statusCode = 407;
_errorMsg = ERR_MSG_407;
break;
case ERROR_RESOURCE_CALL_TIMED_OUT:
_statusCode = 408;
_errorMsg = ERR_MSG_408;
break;
case INET_E_INVALID_REQUEST:
_statusCode = 400;
_errorMsg = ERR_MSG_400;
break;
case E_ABORT:
_statusCode = 412;
_errorMsg = ERR_MSG_412;
break;
default:
_statusCode = 500;
_errorMsg = ERR_MSG_500;
break;
}
}
//CXHR2DataStream
CXHR2DataStream::CXHR2DataStream() :
_pData(nullptr),
_dataSize(0),
_seekIndex(0),
_refCnt(1)
{
}
CXHR2DataStream::~CXHR2DataStream()
{
if(nullptr != _pData)
delete[] _pData;
}
ULONG CXHR2DataStream::Length()
{
return _dataSize;
}
HRESULT CXHR2DataStream::Init(const void *psBuffer, ULONG cbBufferSize)
{
HRESULT hr = S_OK;
if(psBuffer == nullptr || cbBufferSize > REQUEST_BUFFER_MAX) {
hr = E_INVALIDARG;
}
if(SUCCEEDED(hr)) {
_dataSize = cbBufferSize;
_seekIndex = 0;
_pData = new (std::nothrow) BYTE[_dataSize];
if(_pData == nullptr)
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr)) {
memcpy_s(_pData, _dataSize, psBuffer, cbBufferSize);
}
return hr;
}
HRESULT CXHR2DataStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
HRESULT hr = S_OK;
if(pv == nullptr) {
hr = E_INVALIDARG;
}
if(SUCCEEDED(hr)) {
BYTE* pOutput = (BYTE*)pv;
BYTE* _pInput = _pData;
for(*pcbRead = 0; *pcbRead < cb; (*pcbRead)++)
{
if(_seekIndex == _dataSize) {
hr = S_FALSE;
break;
}
pOutput[*pcbRead] = _pInput[*pcbRead];
_seekIndex++;
}
}
return hr;
}
HRESULT CXHR2DataStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
{
HRESULT hr = E_NOTIMPL;
UNREFERENCED_PARAMETER(pv);
UNREFERENCED_PARAMETER(cb);
UNREFERENCED_PARAMETER(pcbWritten);
return hr;
}
ULONG CXHR2DataStream::AddRef()
{
return ::InterlockedIncrement(&_refCnt);
}
ULONG CXHR2DataStream::Release()
{
ULONG refCnt = ::InterlockedDecrement(&_refCnt);
if(0 == refCnt) {
delete this;
}
return refCnt;
}
HRESULT CXHR2DataStream::QueryInterface(REFIID riid, void **ppvObject)
{
HRESULT hr = S_OK;
if(ppvObject == nullptr) {
hr = E_INVALIDARG;
}
void *pObject = nullptr;
if(SUCCEEDED(hr)) {
if(riid == IID_IUnknown) {
pObject = static_cast<IUnknown*>((IDispatch*)this);
}
else if(riid == IID_IDispatch) {
pObject = static_cast<IDispatch*>(this);
}
else if(riid == IID_ISequentialStream) {
pObject = static_cast<ISequentialStream*>(this);
}
else {
hr = E_NOINTERFACE;
}
}
if(SUCCEEDED(hr)) {
AddRef();
*ppvObject = pObject;
pObject = nullptr;
}
return hr;
}
HRESULT CXHR2DataStream::GetTypeInfoCount(unsigned int FAR* pctinfo)
{
HRESULT hr = E_NOTIMPL;
if(pctinfo)
*pctinfo = 0;
return hr;
}
HRESULT CXHR2DataStream::GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo)
{
HRESULT hr = E_NOTIMPL;
if(ppTInfo)
*ppTInfo = 0;
UNREFERENCED_PARAMETER(iTInfo);
UNREFERENCED_PARAMETER(lcid);
return hr;
}
HRESULT CXHR2DataStream::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId)
{
HRESULT hr = DISP_E_UNKNOWNNAME;
UNREFERENCED_PARAMETER(riid);
UNREFERENCED_PARAMETER(rgszNames);
UNREFERENCED_PARAMETER(cNames);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(rgDispId);
return hr;
}
HRESULT CXHR2DataStream::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr)
{
HRESULT hr = S_OK;
UNREFERENCED_PARAMETER(dispIdMember);
UNREFERENCED_PARAMETER(riid);
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(wFlags);
UNREFERENCED_PARAMETER(pDispParams);
UNREFERENCED_PARAMETER(pVarResult);
UNREFERENCED_PARAMETER(pExcepInfo);
UNREFERENCED_PARAMETER(puArgErr);
return hr;
}
// HttpConnection
HttpConnection::HttpConnection() :
_isInitialized(false),
_spXhr(nullptr),
_spXhrCallback(nullptr),
_spXhrRequestData(nullptr),
_pRequest(nullptr),
_timeOutInMs(0)
{
}
HttpConnection::~HttpConnection()
{
}
bool HttpConnection::init(HttpRequest *pRequest, DWORD timeOutInMs)
{
if (_isInitialized || nullptr == pRequest) {
return _isInitialized;
}
HRESULT hr = CoInitializeEx(NULL, NULL);
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(CLSID_FreeThreadedXMLHTTP60, NULL, CLSCTX_SERVER, IID_PPV_ARGS(&_spXhr));
}
if (SUCCEEDED(hr)) {
hr = MakeAndInitialize<CXHR2Callback>(&_spXhrCallback);
}
if(SUCCEEDED(hr)) {
_pRequest = pRequest;
_timeOutInMs = timeOutInMs;
LONG size = _pRequest->getRequestDataSize();
if(size > 0) {
_spXhrRequestData = Make<CXHR2DataStream>();
hr = _spXhrRequestData->Init(_pRequest->getRequestData(), size);
}
}
return _isInitialized = SUCCEEDED(hr);
}
bool HttpConnection::open(std::string verb)
{
return open(verb, false, "");
}
bool HttpConnection::open(std::string verb, bool userAuthentication)
{
return open(verb, userAuthentication, "");
}
bool HttpConnection::open(std::string verb, std::string cookieFile)
{
return open(verb, false, cookieFile);
}
bool HttpConnection::open(std::string verb, bool userAuthentication, std::string cookieFile)
{
if (!_isInitialized) {
return false;
}
std::wstring method(verb.begin(), verb.end());
std::string url(_pRequest->getUrl());
std::wstring wUrl(url.begin(), url.end());
HRESULT hr = _spXhr->Open(method.c_str(), wUrl.c_str(), _spXhrCallback.Get(), NULL, NULL, NULL, NULL);
if(SUCCEEDED(hr) && _timeOutInMs != 0) {
hr = _spXhr->SetProperty(XHR_PROP_TIMEOUT, _timeOutInMs);
}
#if 0
if(SUCCEEDED(hr)) {
hr = _spXhr->SetProperty(XHR_PROP_ONDATA_THRESHOLD, READ_BUFFER_MAX);
}
#endif
auto headers = _pRequest->getHeaders();
formatHeaders(headers);
for(auto header : headers)
{
std::string key = header.substr(0, header.find_first_of(':'));
std::string value = header.substr(header.find_first_of(':') + 1, header.size() - 1);
if(SUCCEEDED(hr)) {
hr = _spXhr->SetRequestHeader(std::wstring(key.begin(), key.end()).c_str(), std::wstring(value.begin(), value.end()).c_str());
}
}
if(SUCCEEDED(hr) && userAuthentication) {
std::string authHeaders = std::accumulate(headers.begin(), headers.end(), std::string(""));
hr = authenticateUser(verb, url, authHeaders);
}
if(SUCCEEDED(hr) && !cookieFile.empty()) {
hr = processCookieFile(url, cookieFile);
}
if(FAILED(hr)) {
cancelRequest(hr);
}
return SUCCEEDED(hr);
}
HRESULT HttpConnection::authenticateUser(std::string& verb, std::string& url, std::string& headers)
{
HRESULT hr = S_OK;
std::string authToken;
std::string authSig;
std::string authBody;
if(_pRequest->getRequestDataSize() > 0)
authBody = _pRequest->getRequestData();
if(getAuthenticationToken(verb, url, headers, authBody, authToken, authSig)) {
hr = _spXhr->SetRequestHeader(L"Authorization", std::wstring(authToken.begin(), authToken.end()).c_str());
if(SUCCEEDED(hr)) {
hr = _spXhr->SetRequestHeader(L"Signature", std::wstring(authSig.begin(), authSig.end()).c_str());
}
}
else
{
hr = INET_E_AUTHENTICATION_REQUIRED;
}
return hr;
}
HRESULT HttpConnection::processCookieFile(std::string& url, std::string& cookieFile)
{
HRESULT hr = S_OK;
HttpCookie cookie;
cookie.setCookieFileName(cookieFile);
auto cookies = cookie.getCookies();
std::string cookieInfo = "";
int cCnt = 0;
for(auto iter = cookies->begin(); iter != cookies->end(); iter++)
{
if(url.find(iter->domain) != std::string::npos)
{
std::string keyVal = iter->name;
keyVal.append("=");
keyVal.append(iter->value);
if(cCnt != 0) {
cookieInfo.append(";");
}
cookieInfo.append(keyVal);
cCnt++;
}
}
if(!cookieInfo.empty() && nullptr != _spXhr) {
hr = _spXhr->SetRequestHeader(L"Cookie", std::wstring(cookieInfo.begin(), cookieInfo.end()).c_str());
}
return hr;
}
bool HttpConnection::send()
{
if (!_isInitialized) {
return false;
}
HRESULT hr = E_FAIL;
if(nullptr == _spXhrRequestData) {
hr = _spXhr->Send(NULL, 0);
}
else {
hr = _spXhr->Send(_spXhrRequestData.Get(), _spXhrRequestData->Length());
}
if(SUCCEEDED(hr)) {
DWORD status = 0;
hr = _spXhrCallback->WaitForComplete(&status);
}
else {
cancelRequest(hr);
}
return SUCCEEDED(hr);
}
DWORD HttpConnection::getStatusCode()
{
return _spXhrCallback != nullptr ? _spXhrCallback->_statusCode : 500;
}
std::string HttpConnection::getErrorMessage()
{
return _spXhrCallback != nullptr ? _spXhrCallback->_errorMsg : ERR_MSG_500;
}
std::vector<char>* HttpConnection::getResponseHeader()
{
return _spXhrCallback != nullptr ? &_spXhrCallback->_headers : nullptr;
}
std::vector<char>* HttpConnection::getResponseData()
{
return _spXhrCallback != nullptr ? &_spXhrCallback->_data : nullptr;
}
void HttpConnection::cancelRequest(HRESULT hrError)
{
if(nullptr != _spXhr) {
_spXhr->Abort();
_spXhrCallback->CompleteRequest(hrError);
}
}
}
NS_CC_END

View File

@ -0,0 +1,157 @@
/****************************************************************************
Copyright (c) 2012 greathqy
Copyright (c) 2012 cocos2d-x.org
Copyright (c) 2013-2014 Chukong Technologies Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
#include "network/HttpRequest.h"
#include "network/HttpClient.h"
#include <thread>
#include <queue>
#include <numeric>
#include <condition_variable>
#include <errno.h>
#include <wrl.h>
#if defined(_XBOX_ONE)
#include <ixmlhttprequest2.h>
#else
#include <Msxml6.h>
#endif
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Details;
#include "base/CCVector.h"
#include "base/CCDirector.h"
#include "base/CCScheduler.h"
#include "platform/CCFileUtils.h"
NS_CC_BEGIN
namespace network {
const int READ_BUFFER_MAX = 16 * 1024;
const int REQUEST_BUFFER_MAX = 4 * 1024 * 1024;
const char ERR_MSG_500[] = "Internal server error";
const char ERR_MSG_400[] = "Invalid request";
const char ERR_MSG_401[] = "Authentication is required to access this resource";
const char ERR_MSG_403[] = "Forbidden";
const char ERR_MSG_404[] = "Specified resource could not be found";
const char ERR_MSG_407[] = "Proxy authentication failed";
const char ERR_MSG_408[] = "Request timed out";
const char ERR_MSG_412[] = "Request aborted";
const char ERR_MSG_DL_FLD[] = "The download of the specified resource has failed";
class CXHR2Callback : public Microsoft::WRL::RuntimeClass<RuntimeClassFlags<ClassicCom>, IXMLHTTPRequest2Callback>
{
public:
CXHR2Callback();
~CXHR2Callback();
HRESULT RuntimeClassInitialize();
HRESULT ReadStreamData(ISequentialStream* pResponseStream);
STDMETHODIMP OnRedirect(IXMLHTTPRequest2 *pXHR, const WCHAR *pwszRedirectUrl);
STDMETHODIMP OnHeadersAvailable(IXMLHTTPRequest2 *pXHR, DWORD dwStatus, const WCHAR *pwszStatus);
STDMETHODIMP OnDataAvailable(IXMLHTTPRequest2 *pXHR, ISequentialStream *pResponseStream);
STDMETHODIMP OnResponseReceived(IXMLHTTPRequest2 *pXHR, ISequentialStream *pResponseStream);
STDMETHODIMP OnError(IXMLHTTPRequest2 *pXHR, HRESULT hrError);
STDMETHODIMP WaitForComplete(PDWORD pdwStatus);
protected:
void CompleteRequest(HRESULT hrError);
private:
HANDLE _hWfC;
DWORD _statusCode;
std::string _errorMsg;
std::vector<char> _headers;
std::vector<char> _data;
friend class HttpConnection;
};
class CXHR2DataStream : public Microsoft::WRL::RuntimeClass<RuntimeClassFlags<ClassicCom>, ISequentialStream, IDispatch>
{
public:
CXHR2DataStream();
~CXHR2DataStream();
STDMETHODIMP_(ULONG) Length();
STDMETHODIMP Init(const void *psBuffer, ULONG cbBufferSize);
STDMETHODIMP Read(void *pv, ULONG cb, ULONG *pcbRead);
STDMETHODIMP Write(const void *pv, ULONG cb, ULONG *pcbWritten);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo);
STDMETHODIMP GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo);
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId);
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr);
private:
ULONG _refCnt;
BYTE* _pData;
ULONG _dataSize;
ULONG _seekIndex;
};
class HttpConnection
{
public:
HttpConnection();
~HttpConnection();
bool init(HttpRequest *pRequest, DWORD timeOutInMs = 0);
bool open(std::string verb);
bool open(std::string verb, bool userAuthentication);
bool open(std::string verb, std::string cookieFile);
bool open(std::string verb, bool userAuthentication, std::string cookieFile);
bool send();
DWORD getStatusCode();
std::string getErrorMessage();
std::vector<char>* getResponseHeader();
std::vector<char>* getResponseData();
protected:
void cancelRequest(HRESULT hrError);
HRESULT authenticateUser(std::string& verb, std::string& url, std::string& headers);
HRESULT processCookieFile(std::string& url, std::string& cookieFile);
private:
bool _isInitialized;
ComPtr<IXMLHTTPRequest2> _spXhr;
ComPtr<CXHR2Callback> _spXhrCallback;
ComPtr<CXHR2DataStream> _spXhrRequestData;
DWORD _timeOutInMs;
cocos2d::network::HttpRequest* _pRequest;
};
}
NS_CC_END