diff --git a/cocos/network/HttpClient-winrt.cpp b/cocos/network/HttpClient-winrt.cpp new file mode 100644 index 0000000000..83949dc7d9 --- /dev/null +++ b/cocos/network/HttpClient-winrt.cpp @@ -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 +#include +#include + +#include + +#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* s_requestQueue = nullptr; + static Vector* 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 *pFromBuffer, std::vector *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 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 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(); + s_responseQueue = new (std::nothrow) Vector(); + + 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 diff --git a/cocos/network/HttpConnection-winrt.cpp b/cocos/network/HttpConnection-winrt.cpp new file mode 100644 index 0000000000..d44901197c --- /dev/null +++ b/cocos/network/HttpConnection-winrt.cpp @@ -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& 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^ body; + + if(!bodyXST.empty()) { + body = ref new Platform::Array((unsigned char*)bodyXST.c_str(), bodyXST.size()); + } + else { + body = ref new Platform::Array(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( + [&token, &signature, &bRet, &hr](Windows::Foundation::IAsyncOperation^ 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((IDispatch*)this); + } + else if(riid == IID_IDispatch) { + pObject = static_cast(this); + } + else if(riid == IID_ISequentialStream) { + pObject = static_cast(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(&_spXhrCallback); + } + + if(SUCCEEDED(hr)) { + _pRequest = pRequest; + _timeOutInMs = timeOutInMs; + + LONG size = _pRequest->getRequestDataSize(); + + if(size > 0) { + _spXhrRequestData = Make(); + 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* HttpConnection::getResponseHeader() + { + return _spXhrCallback != nullptr ? &_spXhrCallback->_headers : nullptr; + } + + std::vector* HttpConnection::getResponseData() + { + return _spXhrCallback != nullptr ? &_spXhrCallback->_data : nullptr; + } + + void HttpConnection::cancelRequest(HRESULT hrError) + { + if(nullptr != _spXhr) { + _spXhr->Abort(); + _spXhrCallback->CompleteRequest(hrError); + } + } +} + +NS_CC_END diff --git a/cocos/network/HttpConnection-winrt.h b/cocos/network/HttpConnection-winrt.h new file mode 100644 index 0000000000..db757fe424 --- /dev/null +++ b/cocos/network/HttpConnection-winrt.h @@ -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 +#include +#include +#include + +#include + +#include +#if defined(_XBOX_ONE) +#include +#else +#include +#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, 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 _headers; + std::vector _data; + + friend class HttpConnection; + }; + + class CXHR2DataStream : public Microsoft::WRL::RuntimeClass, 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* getResponseHeader(); + std::vector* 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 _spXhr; + ComPtr _spXhrCallback; + ComPtr _spXhrRequestData; + DWORD _timeOutInMs; + cocos2d::network::HttpRequest* _pRequest; + }; +} + +NS_CC_END