From b094f70ab314984a14b5787fc660ea68e500bc30 Mon Sep 17 00:00:00 2001 From: Walzer Date: Tue, 7 Aug 2012 18:28:07 +0800 Subject: [PATCH] issue #1424, http-get works ok. Tomorrow I will focus on post. --- .../Network/CCHttpRequest/CCHttpRequest.cpp | 576 ------------------ .../Network/CCHttpRequest/CCHttpRequest.h | 193 ------ extensions/Network/HttpClient.cpp | 480 +++++++++++++++ extensions/Network/HttpClient.h | 127 ++++ extensions/Network/HttpRequest.h | 175 ++++++ extensions/Network/HttpResponse.h | 121 ++++ extensions/cocos-ext.h | 4 +- .../Classes/ExtensionsTest/ExtensionsTest.cpp | 2 +- .../HttpRequestTest.cpp | 135 ++-- .../HttpRequestTest.h | 0 .../project.pbxproj.REMOVED.git-id | 2 +- 11 files changed, 995 insertions(+), 820 deletions(-) delete mode 100644 extensions/Network/CCHttpRequest/CCHttpRequest.cpp delete mode 100644 extensions/Network/CCHttpRequest/CCHttpRequest.h create mode 100644 extensions/Network/HttpClient.cpp create mode 100644 extensions/Network/HttpClient.h create mode 100644 extensions/Network/HttpRequest.h create mode 100644 extensions/Network/HttpResponse.h rename samples/TestCpp/Classes/ExtensionsTest/{HttpRequestTest => NetworkTest}/HttpRequestTest.cpp (62%) rename samples/TestCpp/Classes/ExtensionsTest/{HttpRequestTest => NetworkTest}/HttpRequestTest.h (100%) diff --git a/extensions/Network/CCHttpRequest/CCHttpRequest.cpp b/extensions/Network/CCHttpRequest/CCHttpRequest.cpp deleted file mode 100644 index af2ec811ed..0000000000 --- a/extensions/Network/CCHttpRequest/CCHttpRequest.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/**************************************************************************** - Copyright (c) 2010-2012 cocos2d-x.org - Copyright (c) 2012 greathqy - - 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 "CCHttpRequest.h" -#include -#include -#include -#include -#include "platform/CCThread.h" -#include "curl/curl.h" - -NS_CC_EXT_BEGIN - -static pthread_t s_requestThread; -static pthread_mutex_t s_requestQueueMutex; -static pthread_mutex_t s_responseQueueMutex; -static sem_t * s_pSem = NULL; -static unsigned long s_asyncRequestCount = 0; - -#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS -#define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 1 -#else -#define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 0 -#endif - -#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE -#define CC_ASYNC_HTTPREQUEST_SEMAPHORE "ccHttpAsync" -#else -static sem_t s_sem; -#endif - -static bool need_quit = false; - -static std::queue *s_requestQueue = NULL; -static std::queue *s_responseQueue = NULL; - -static CCHttpRequest *_g_singleton_httprequest = NULL; - -static char errorBuffer[CURL_ERROR_SIZE]; -typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream); - -//Callback function used by libcurl for save file -size_t writeFile(void *ptr, size_t size, size_t nmemb, void *stream) -{ - int written = fwrite(ptr, size, nmemb, (FILE *)stream); - - return written; -} - -//Callback function used by libcurl for collect response data -size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream) -{ - std::string *str = (std::string *)stream; - - size_t sizes = size * nmemb; - str->append((char *)ptr, sizes); - - return sizes; -} - -//Prototypes -void releaseRequestQueue(); -bool configureCURL(CURL *handle); -int processGetTask(HttpRequestPacket *task, write_callback callback, void *stream, int32_t *responseCode); -int processPostTask(HttpRequestPacket *task, write_callback callback, void *stream, int32_t *responseCode); -int processDownloadTask(HttpRequestPacket *task, write_callback callback, void *stream, int32_t *responseCode); - -void CCHttpRequest::purgeSharedHttpRequest() -{ - CC_SAFE_RELEASE_NULL(_g_singleton_httprequest); -} - -CCHttpRequest *CCHttpRequest::sharedHttpRequest() -{ - if (_g_singleton_httprequest == NULL) { - _g_singleton_httprequest = new CCHttpRequest(); - } - - return _g_singleton_httprequest; -} - -//Worker thread -static void *requestThread(void *data) -{ - CCThread thread; - thread.createAutoreleasePool(); - - HttpRequestPacket *req = NULL; - - while (true) { - //Wait for http request tasks from main thread - int semWaitRet = sem_wait(s_pSem); - if (semWaitRet < 0) { - CCLog("HttpRequest async thread semaphore error: %s\n", strerror(errno)); - break; - } - - std::queue *pQueue = s_requestQueue; - pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue - if (pQueue->empty()) { - pthread_mutex_unlock(&s_requestQueueMutex); - - if (need_quit) { - break; - } else { - continue; - } - } else { - if (need_quit) { - pthread_mutex_unlock(&s_requestQueueMutex); - break; - } - - req = pQueue->front(); - pQueue->pop(); - pthread_mutex_unlock(&s_requestQueueMutex); - } - - //Create a response packet and assume it will successed - HttpResponsePacket *responsePacket = new HttpResponsePacket(); - responsePacket->request = req; - responsePacket->succeed = true; - - //Process the request - if (req->reqType == kHttpRequestGet) { //Get Request - int32_t ret = processGetTask(req, writeData, &responsePacket->responseData, &responsePacket->responseCode); - if (ret != 0) { - responsePacket->succeed = false; - responsePacket->responseData = errorBuffer; - } - } else if (req->reqType == kHttpRequestPost) { //Post Request - int32_t ret = processPostTask(req, writeData, &responsePacket->responseData, &responsePacket->responseCode); - if (ret != 0) { - responsePacket->succeed = false; - responsePacket->responseData = errorBuffer; - } - } else if (req->reqType == kHttpRequestDownloadFile) { //Download File Request - bool fullyDownloaded = true; - std::vector::iterator iter; - std::string saveFileName; - std::string needDownload; - - for (iter = req->files.begin(); iter != req->files.end(); ++iter) { - needDownload = *iter; - std::string::size_type pos = needDownload.rfind("/"); - - if (pos != std::string::npos) { - saveFileName = needDownload.substr(pos + 1); - } else { - saveFileName = needDownload; - } - - //If the download url is http://www.xxx.com/yyy.html - //The saved file name must be yyy.html - saveFileName = CCFileUtils::sharedFileUtils()->getWriteablePath() + saveFileName; - FILE *handle = fopen(saveFileName.c_str(), "w+"); - if (!handle) { - fullyDownloaded = false; - break; - } - req->url = needDownload; - int32_t ret = processDownloadTask(req, writeFile, handle, &responsePacket->responseCode); - if (handle) { - fclose(handle); - } - if (ret != 0) { - fullyDownloaded = false; - break; - } - } - - //Only consider download task successfully when all the files downloaded - if (!fullyDownloaded) { - responsePacket->succeed = false; - responsePacket->responseData = errorBuffer; - } - } - - pthread_mutex_lock(&s_responseQueueMutex); - s_responseQueue->push(responsePacket); - pthread_mutex_unlock(&s_responseQueueMutex); - } - - //If worker thread received quit signal, clean up un-completed request queue - releaseRequestQueue(); - - if (s_pSem != NULL) { -#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE - sem_unlink(CC_ASYNC_HTTPREQUEST_SEMAPHORE); - sem_close(s_pSem); -#else - sem_destroy(s_pSem); -#endif - - s_pSem = NULL; - - pthread_mutex_destroy(&s_requestQueueMutex); - pthread_mutex_destroy(&s_responseQueueMutex); - - delete s_requestQueue; - delete s_responseQueue; - } - - pthread_exit(NULL); - - return 0; -} - -//Release Http request task queue -void releaseRequestQueue() -{ - pthread_mutex_lock(&s_requestQueueMutex); - - int32_t requestQueueSize = s_requestQueue->size(); - if (requestQueueSize > 0) { - for (int32_t i = 0; i < requestQueueSize; ++i) { - HttpRequestPacket *packet = s_requestQueue->front(); - s_requestQueue->pop(); - - delete packet; - } - - s_asyncRequestCount -= requestQueueSize; - } - - pthread_mutex_unlock(&s_requestQueueMutex); -} - -//Configure curl's timeout property -bool configureCURL(CURL *handle) -{ - if (!handle) { - return false; - } - - int32_t code; - code = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errorBuffer); - if (code != CURLE_OK) { - return false; - } - code = curl_easy_setopt(handle, CURLOPT_TIMEOUT, CCHttpRequest::sharedHttpRequest()->getDownloadTimeout()); - if (code != CURLE_OK) { - return false; - } - code = curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, CCHttpRequest::sharedHttpRequest()->getConnectTimeout()); - if (code != CURLE_OK) { - return false; - } - - return true; -} - -//Process Get Request -int processGetTask(HttpRequestPacket *task, write_callback callback, void *stream, int32_t *responseCode) -{ - CURLcode code = CURL_LAST; - CURL *curl = curl_easy_init(); - - do { - if (!configureCURL(curl)) { - break; - } - - code = curl_easy_setopt(curl, CURLOPT_URL, task->url.c_str()); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream); - if (code != CURLE_OK) { - break; - } - code = curl_easy_perform(curl); - if (code != CURLE_OK) { - break; - } - - code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode); - if (code != CURLE_OK || *responseCode / 100 != 2) { - code = CURLE_HTTP_RETURNED_ERROR; - } - } while (0); - if (curl) { - curl_easy_cleanup(curl); - } - - return (code == CURLE_OK ? 0 : 1); -} - -//Process POST Request -int processPostTask(HttpRequestPacket *task, write_callback callback, void *stream, int32_t *responseCode) -{ - CURLcode code = CURL_LAST; - CURL *curl = curl_easy_init(); - - do { - if (!configureCURL(curl)) { - break; - } - - code = curl_easy_setopt(curl, CURLOPT_URL, task->url.c_str()); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_POST, 1); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, task->reqData.data()); - if (code != CURLE_OK) { - break; - } - code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, task->reqData.size()); - if (code != CURLE_OK) { - break; - } - code = curl_easy_perform(curl); - if (code != CURLE_OK) { - break; - } - - code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode); - if (code != CURLE_OK || *responseCode / 100 != 2) { - code = CURLE_HTTP_RETURNED_ERROR; - } - } while (0); - if (curl) { - curl_easy_cleanup(curl); - } - - return (code == CURLE_OK ? 0 : 1); -} - -//Process Download Request -int processDownloadTask(HttpRequestPacket *task, write_callback callback, void *stream, int32_t *responseCode) -{ - return processGetTask(task, callback, stream, responseCode); -} - -CCHttpRequest::CCHttpRequest() -{ - m_timeout = 60; - m_connectTimeout = 10; -} - -CCHttpRequest::~CCHttpRequest() -{ - need_quit = true; - - if (s_pSem != NULL) { - sem_post(s_pSem); - } -} - -//Lazy create semaphore & mutex & thread -bool CCHttpRequest::lazyInitThreadSemphore() -{ - if (s_pSem != NULL) { - return true; - } else { -#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE - s_pSem = sem_open(CC_ASYNC_HTTPREQUEST_SEMAPHORE, O_CREAT, 0644, 0); - if (s_pSem == SEM_FAILED) { - CCLog("Open HttpRequest Semaphore failed"); - s_pSem = NULL; - return false; - } -#else - int semRet = sem_init(&s_sem, 0, 0); - if (semRet < 0) { - CCLog("Init HttpRequest Semaphore failed"); - return false; - } - - s_pSem = &s_sem; -#endif - - s_requestQueue = new std::queue(); - s_responseQueue = new std::queue(); - - pthread_mutex_init(&s_requestQueueMutex, NULL); - pthread_mutex_init(&s_responseQueueMutex, NULL); - - pthread_create(&s_requestThread, NULL, requestThread, NULL); - pthread_detach(s_requestThread); - - need_quit = false; - } - - return true; -} - -//Add a get task to queue -void CCHttpRequest::addGetTask(std::string url, cocos2d::CCObject *pTarget, SEL_CallFuncND pSelector) -{ - bool inited = lazyInitThreadSemphore(); - if (!inited) { - return; - } - - if (0 == s_asyncRequestCount) { - CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(CCHttpRequest::dispatchResponseCallbacks), this, 0, false); - } - - ++s_asyncRequestCount; - if (pTarget) { - pTarget->retain(); - } - - HttpRequestPacket *packet = new HttpRequestPacket(); - packet->reqType = kHttpRequestGet; - packet->reqId = this->reqId; - packet->url = url; - packet->pTarget = pTarget; - packet->pSelector = pSelector; - - this->reqId.clear(); - - pthread_mutex_lock(&s_requestQueueMutex); - s_requestQueue->push(packet); - pthread_mutex_unlock(&s_requestQueueMutex); - - //Notify thread start to work - sem_post(s_pSem); -} - -//Add a post task to queue -void CCHttpRequest::addPostTask(std::string &url, std::string &postData, CCObject *pTarget, SEL_CallFuncND pSelector) -{ - bool inited = lazyInitThreadSemphore(); - if (!inited) { - return; - } - - if (0 == s_asyncRequestCount) { - CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(CCHttpRequest::dispatchResponseCallbacks), this, 0, false); - } - - ++s_asyncRequestCount; - if (pTarget) { - pTarget->retain(); - } - - HttpRequestPacket *packet = new HttpRequestPacket(); - packet->reqType = kHttpRequestPost; - packet->reqId = this->reqId; - packet->url = url; - packet->reqData = postData; - packet->pTarget = pTarget; - packet->pSelector = pSelector; - - this->reqId.clear(); - - pthread_mutex_lock(&s_requestQueueMutex); - s_requestQueue->push(packet); - pthread_mutex_unlock(&s_requestQueueMutex); - - //Notify thread start to work - sem_post(s_pSem); -} - -//Add post task to queue, data binary safe -void CCHttpRequest::addPostTask(std::string &url, std::vector &buffer, cocos2d::CCObject *pTarget, SEL_CallFuncND pSelector) -{ - std::string postData = std::string((const char *)&buffer.front(), buffer.size()); - addPostTask(url, postData, pTarget, pSelector); -} - -//Convert const char* to string and call the matched addPostTask method -void CCHttpRequest::addPostTask(std::string &url, const char *buffer, int32_t bufferLen, cocos2d::CCObject *pTarget, SEL_CallFuncND pSelector) -{ - std::string postData = std::string(buffer, bufferLen); - addPostTask(url, postData, pTarget, pSelector); -} - -//Add a download task to queue -void CCHttpRequest::addDownloadTask(std::vector &urls, CCObject *pTarget, SEL_CallFuncND pSelector) -{ - bool init = lazyInitThreadSemphore(); - if (!init) { - return; - } - - if (0 == s_asyncRequestCount) { - CCDirector::sharedDirector()->getScheduler()->scheduleSelector(schedule_selector(CCHttpRequest::dispatchResponseCallbacks), this, 0, false); - } - - ++s_asyncRequestCount; - if (pTarget) { - pTarget->retain(); - } - - HttpRequestPacket *packet = new HttpRequestPacket(); - packet->reqType = kHttpRequestDownloadFile; - packet->reqId = this->reqId; - packet->files = urls; - packet->pTarget = pTarget; - packet->pSelector = pSelector; - - pthread_mutex_lock(&s_requestQueueMutex); - s_requestQueue->push(packet); - pthread_mutex_unlock(&s_requestQueueMutex); - - sem_post(s_pSem); -} - -//Poll and notify main thread if responses exists in queue -void CCHttpRequest::dispatchResponseCallbacks(float delta) -{ - std::queue *pQueue = s_responseQueue; - - pthread_mutex_lock(&s_responseQueueMutex); - if (pQueue->empty()) { - pthread_mutex_unlock(&s_responseQueueMutex); - } else { - HttpResponsePacket *packet = pQueue->front(); - pQueue->pop(); - - pthread_mutex_unlock(&s_responseQueueMutex); - - --s_asyncRequestCount; - if (0 == s_asyncRequestCount) { - CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCHttpRequest::dispatchResponseCallbacks), this); - } - - HttpRequestPacket *orgRequest = packet->request; - CCObject *pTarget = orgRequest->pTarget; - SEL_CallFuncND pSelector = orgRequest->pSelector; - - if (pTarget && pSelector) { - (pTarget->*pSelector)((CCNode *)this, packet); - - pTarget->release(); - } - - delete orgRequest; - delete packet; - } -} - -NS_CC_EXT_END - - diff --git a/extensions/Network/CCHttpRequest/CCHttpRequest.h b/extensions/Network/CCHttpRequest/CCHttpRequest.h deleted file mode 100644 index b9fd5bd66e..0000000000 --- a/extensions/Network/CCHttpRequest/CCHttpRequest.h +++ /dev/null @@ -1,193 +0,0 @@ -/**************************************************************************** - Copyright (c) 2010-2012 cocos2d-x.org - Copyright (c) 2012 greathqy - - 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. - ****************************************************************************/ - -#ifndef __CCHTTPREQUEST_H__ -#define __CCHTTPREQUEST_H__ - -#include "cocos2d.h" -#include "ExtensionMacros.h" - -NS_CC_EXT_BEGIN - -/** - * @addtogroup Network - * @{ - */ - -//Http request type -typedef enum { - kHttpRequestGet, - kHttpRequestPost, - kHttpRequestDownloadFile, -} HttpRequestType; - -//Request structure -typedef struct { - HttpRequestType reqType; - std::string url; - std::string reqData; - std::vector files; - std::string reqId; - cocos2d::CCObject *pTarget; - cocos2d::SEL_CallFuncND pSelector; -} HttpRequestPacket; - -//Response structure -typedef struct { - HttpRequestPacket *request; - std::string responseData; - int32_t responseCode; - bool succeed; -} HttpResponsePacket; - -/** @brief Singleton that handles asynchrounous http requests - * Once the request completed, a callback will issued in main thread when it provided during make request - */ -class CC_DLL CCHttpRequest : public cocos2d::CCObject -{ -public: - /** Return the shared instance **/ - static CCHttpRequest *sharedHttpRequest(); - - /** Relase the shared instance **/ - static void purgeSharedHttpRequest(); - - CCHttpRequest(); - virtual ~CCHttpRequest(); - - /** - * Add a get request to task queue - * @param url url want to get - * @param pTarget callback target - * @param pSelector callback selector - * @return NULL - */ - void addGetTask(std::string url, cocos2d::CCObject *pTarget, cocos2d::SEL_CallFuncND pSelector); - - /** - * Add a post request to task queue - * @param url the post destination url - * @param postData data want to post - * @param pTarget callback target - * @param pSelector callback selector - * @return NULL - */ - void addPostTask(std::string &url, std::string &postData, cocos2d::CCObject *pTarget, cocos2d::SEL_CallFuncND pSelector); - - /** - * Add a post request to task queue, binary data supported - * @param url the post destination url - * @param buffer the data want to post - * @param pTarget callback target - * @param pSelector callback selector - * @return NULL - */ - void addPostTask(std::string &url, std::vector &buffer, cocos2d::CCObject *pTarget, cocos2d::SEL_CallFuncND pSelector); - - /** - * Add a post request to task queue, binary data supported - * @param url the post destination url - * @param buffer the data want to post - * @param buffer the data's length - * @param pTarget callback target - * @param pSelector callback selector - * @return NULL - */ - void addPostTask(std::string &url, const char *buffer, int32_t bufferLen, cocos2d::CCObject *pTarget, cocos2d::SEL_CallFuncND pSelector); - - /** - * Add a download request to task queue - * @param url urls want to download - * @param pTarget callback target - * @param pSelector callback selector - * @return NULL - */ - void addDownloadTask(std::vector &urls, cocos2d::CCObject *pTarget, cocos2d::SEL_CallFuncND pSelector); - -public: - /** - * Change the connect timeout - * @param timeout - * @return NULL - */ - inline void setConnectTimeout(int32_t timeout) {m_connectTimeout = timeout;}; - - /** - * Get connect timeout - * @return int - */ - inline int32_t getConnectTimeout() {return m_connectTimeout;} - - - /** - * Change the download timeout - * @param timeout - * @return NULL - */ - inline void setDownloadTimeout(int32_t timeout) {m_timeout = timeout;}; - - - /** - * Get download timeout - * @return int - */ - inline int32_t getDownloadTimeout() {return m_timeout;} - - /** - * Set request Id - * Used to distinguish multple requests if you don't want to provide different callbacks for each request - * @param reqId request identifier - * @return NULL - */ - inline void setReqId(const std::string reqId) {this->reqId = reqId;} - - /** - * Get request id - * @return NULL - */ - inline const std::string& getReqId() {return this->reqId;}; - -private: - /** - * Init pthread mutex, semaphore, and create new thread for http requests - * @return bool - */ - bool lazyInitThreadSemphore(); - /** Poll function called from main thread to dispatch callbacks when http requests finished **/ - void dispatchResponseCallbacks(float delta); - -private: - int32_t m_connectTimeout; - int32_t m_timeout; - - std::string reqId; -}; - -// end of Network group -/// @} - -NS_CC_EXT_END - -#endif //__CCHTTPREQUEST_H__ diff --git a/extensions/Network/HttpClient.cpp b/extensions/Network/HttpClient.cpp new file mode 100644 index 0000000000..56152fa80e --- /dev/null +++ b/extensions/Network/HttpClient.cpp @@ -0,0 +1,480 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2012 greathqy + + 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 "platform/CCThread.h" + +#include +#include +#include +#include + +#include "curl/curl.h" + +NS_CC_EXT_BEGIN + +static pthread_t s_networkThread; +static pthread_mutex_t s_requestQueueMutex; +static pthread_mutex_t s_responseQueueMutex; +static sem_t * s_pSem = NULL; +static unsigned long s_asyncRequestCount = 0; + +#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS +#define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 1 +#else +#define CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE 0 +#endif + +#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE +#define CC_ASYNC_HTTPREQUEST_SEMAPHORE "ccHttpAsync" +#else +static sem_t s_sem; +#endif + +static bool need_quit = false; + +static CCArray* s_requestQueue = NULL; +static CCArray* s_responseQueue = NULL; + +static CCHttpClient *s_pHttpClient = NULL; // pointer to singleton + +static char s_errorBuffer[CURL_ERROR_SIZE]; + +typedef size_t (*write_callback)(void *ptr, size_t size, size_t nmemb, void *stream); + + +// Callback function used by libcurl for collect response data +size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream) +{ + std::vector *recvBuffer = (std::vector*)stream; + size_t sizes = size * nmemb; + + recvBuffer->clear(); + recvBuffer->assign((char*)ptr, (char*)ptr + sizes); + + return sizes; +} + +// Prototypes +bool configureCURL(CURL *handle); +int processGetTask(HttpRequest *task, write_callback callback, void *stream, int32_t *errorCode); +int processPostTask(HttpRequest *task, write_callback callback, void *stream, int32_t *errorCode); +// int processDownloadTask(HttpRequest *task, write_callback callback, void *stream, int32_t *errorCode); + + +//Worker thread +static void* networkThread(void *data) +{ + HttpRequest *request = NULL; + + while (true) + { + // Wait for http request tasks from main thread + int semWaitRet = sem_wait(s_pSem); + if (semWaitRet < 0) { + CCLog("HttpRequest async thread semaphore error: %s\n", strerror(errno)); + break; + } + + if (need_quit) + { + break; + } + + // step 1: send http request if the requestQueue isn't empty + request = NULL; + + pthread_mutex_lock(&s_requestQueueMutex); //Get request task from queue + if (0 != s_requestQueue->count()) + { + request = dynamic_cast(s_requestQueue->objectAtIndex(0)); + s_requestQueue->removeObjectAtIndex(0); + } + pthread_mutex_unlock(&s_requestQueueMutex); + + if (NULL == request) + { + continue; + } + + // step 2: libcurl sync access + + // Create a HttpResponse object, the default setting is http access failed + HttpResponse *response = new HttpResponse(request); + + int responseCode = CURL_LAST; + int retValue = 0; + + // Process the request -> get response packet + switch (request->getRequestType()) + { + case HttpRequest::kHttpGet: // HTTP GET + retValue = processGetTask(request, + writeData, + response->getResponseData(), + &responseCode); + break; + + case HttpRequest::kHttpPost: // HTTP POST + retValue = processPostTask(request, + writeData, + response->getResponseData(), + &responseCode); + break; + + default: + CCAssert(true, "CCHttpClient: unkown request type, only GET and POSt are supported"); + break; + } + + response->setResponseCode(responseCode); + + if (retValue != 0) + { + response->setSucceed(false); + response->setErrorBuffer(s_errorBuffer); + } + else + { + response->setSucceed(true); + } + + // add response packet into queue + pthread_mutex_lock(&s_responseQueueMutex); + s_responseQueue->addObject(response); + pthread_mutex_unlock(&s_responseQueueMutex); + + // resume dispatcher selector + CCDirector::sharedDirector()->getScheduler()->resumeTarget(CCHttpClient::getInstance()); + } + + // cleanup: if worker thread received quit signal, clean up un-completed request queue + pthread_mutex_lock(&s_requestQueueMutex); + + CCObject* pObj = NULL; + CCARRAY_FOREACH(s_requestQueue, pObj) + { + HttpRequest* request = dynamic_cast(pObj); + request->getTarget()->release(); + s_asyncRequestCount--; + } + s_requestQueue->removeAllObjects(); + + pthread_mutex_unlock(&s_requestQueueMutex); + + + if (s_pSem != NULL) { +#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE + sem_unlink(CC_ASYNC_HTTPREQUEST_SEMAPHORE); + sem_close(s_pSem); +#else + sem_destroy(s_pSem); +#endif + + s_pSem = NULL; + + pthread_mutex_destroy(&s_requestQueueMutex); + pthread_mutex_destroy(&s_responseQueueMutex); + + s_requestQueue->release(); + s_responseQueue->release(); + } + + pthread_exit(NULL); + + return 0; +} + +//Configure curl's timeout property +bool configureCURL(CURL *handle) +{ + if (!handle) { + return false; + } + + int32_t code; + code = curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, s_errorBuffer); + if (code != CURLE_OK) { + return false; + } + code = curl_easy_setopt(handle, CURLOPT_TIMEOUT, CCHttpClient::getInstance()->getTimeoutForRead()); + if (code != CURLE_OK) { + return false; + } + code = curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, CCHttpClient::getInstance()->getTimeoutForConnect()); + if (code != CURLE_OK) { + return false; + } + + return true; +} + +//Process Get Request +int processGetTask(HttpRequest *request, write_callback callback, void *stream, int *responseCode) +{ + CURLcode code = CURL_LAST; + CURL *curl = curl_easy_init(); + + do { + if (!configureCURL(curl)) + { + break; + } + + code = curl_easy_setopt(curl, CURLOPT_URL, request->getUrl()); + if (code != CURLE_OK) + { + break; + } + + code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback); + if (code != CURLE_OK) + { + break; + } + + code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream); + if (code != CURLE_OK) + { + break; + } + + code = curl_easy_perform(curl); + if (code != CURLE_OK) + { + break; + } + + code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode); + if (code != CURLE_OK || *responseCode != 200) + { + code = CURLE_HTTP_RETURNED_ERROR; + } + } while (0); + + if (curl) { + curl_easy_cleanup(curl); + } + + return (code == CURLE_OK ? 0 : 1); +} + +//Process POST Request +int processPostTask(HttpRequest *request, write_callback callback, void *stream, int32_t *responseCode) +{ + CURLcode code = CURL_LAST; + CURL *curl = curl_easy_init(); + + do { + if (!configureCURL(curl)) { + break; + } + + code = curl_easy_setopt(curl, CURLOPT_URL, request->getUrl()); + if (code != CURLE_OK) { + break; + } + code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback); + if (code != CURLE_OK) { + break; + } + code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, stream); + if (code != CURLE_OK) { + break; + } + code = curl_easy_setopt(curl, CURLOPT_POST, 1); + if (code != CURLE_OK) { + break; + } + code = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request->getRequestData()); + if (code != CURLE_OK) { + break; + } + code = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request->getRequestDataSize()); + if (code != CURLE_OK) { + break; + } + code = curl_easy_perform(curl); + if (code != CURLE_OK) { + break; + } + + code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, responseCode); + if (code != CURLE_OK || *responseCode != 200) { + code = CURLE_HTTP_RETURNED_ERROR; + } + } while (0); + if (curl) { + curl_easy_cleanup(curl); + } + + return (code == CURLE_OK ? 0 : 1); +} + +// HttpClient implementation +CCHttpClient* CCHttpClient::getInstance() +{ + if (s_pHttpClient == NULL) { + s_pHttpClient = new CCHttpClient(); + } + + return s_pHttpClient; +} + +void CCHttpClient::destroyInstance() +{ + CC_SAFE_RELEASE_NULL(s_pHttpClient); +} + +CCHttpClient::CCHttpClient() +:_timeoutForRead(60) +,_timeoutForConnect(30) +{ + CCDirector::sharedDirector()->getScheduler()->scheduleSelector( + schedule_selector(CCHttpClient::dispatchResponseCallbacks), this, 0, false); + CCDirector::sharedDirector()->getScheduler()->pauseTarget(this); +} + +CCHttpClient::~CCHttpClient() +{ + need_quit = true; + + if (s_pSem != NULL) { + sem_post(s_pSem); + } + + CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCHttpClient::dispatchResponseCallbacks), this); +} + +//Lazy create semaphore & mutex & thread +bool CCHttpClient::lazyInitThreadSemphore() +{ + if (s_pSem != NULL) { + return true; + } else { +#if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE + s_pSem = sem_open(CC_ASYNC_HTTPREQUEST_SEMAPHORE, O_CREAT, 0644, 0); + if (s_pSem == SEM_FAILED) { + CCLog("Open HttpRequest Semaphore failed"); + s_pSem = NULL; + return false; + } +#else + int semRet = sem_init(&s_sem, 0, 0); + if (semRet < 0) { + CCLog("Init HttpRequest Semaphore failed"); + return false; + } + + s_pSem = &s_sem; +#endif + + s_requestQueue = new CCArray(); + s_requestQueue->init(); + + s_responseQueue = new CCArray(); + s_responseQueue->init(); + + pthread_mutex_init(&s_requestQueueMutex, NULL); + pthread_mutex_init(&s_responseQueueMutex, NULL); + + pthread_create(&s_networkThread, NULL, networkThread, NULL); + pthread_detach(s_networkThread); + + need_quit = false; + } + + return true; +} + +//Add a get task to queue +void CCHttpClient::send(HttpRequest* request) +{ + if (false == lazyInitThreadSemphore()) + { + return; + } + + if (!request) + { + return; + } + + ++s_asyncRequestCount; + + request->retain(); + if (request->getTarget()) + { + request->getTarget()->retain(); + } + + pthread_mutex_lock(&s_requestQueueMutex); + s_requestQueue->addObject(request); + pthread_mutex_unlock(&s_requestQueueMutex); + + // Notify thread start to work + sem_post(s_pSem); +} + +// Poll and notify main thread if responses exists in queue +void CCHttpClient::dispatchResponseCallbacks(float delta) +{ + CCLog("CCHttpClient::dispatchResponseCallbacks is running"); + HttpResponse* response = NULL; + + pthread_mutex_lock(&s_responseQueueMutex); + if (s_responseQueue->count()) + { + response = dynamic_cast(s_responseQueue->objectAtIndex(0)); + s_responseQueue->removeObjectAtIndex(0); + } + pthread_mutex_unlock(&s_responseQueueMutex); + + if (response) + { + --s_asyncRequestCount; + + HttpRequest *request = response->getHttpRequest(); + CCObject *pTarget = request->getTarget(); + SEL_CallFuncND pSelector = request->getSelector(); + + if (pTarget && pSelector) + { + (pTarget->*pSelector)((CCNode *)this, response); + } + + response->release(); + } + + if (0 == s_asyncRequestCount) + { + CCDirector::sharedDirector()->getScheduler()->pauseTarget(this); + } + +} + +NS_CC_EXT_END + + diff --git a/extensions/Network/HttpClient.h b/extensions/Network/HttpClient.h new file mode 100644 index 0000000000..f78f5d0407 --- /dev/null +++ b/extensions/Network/HttpClient.h @@ -0,0 +1,127 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + Copyright (c) 2012 greathqy + + 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. + ****************************************************************************/ + +#ifndef __CCHTTPREQUEST_H__ +#define __CCHTTPREQUEST_H__ + +#include "cocos2d.h" +#include "ExtensionMacros.h" + +#include "HttpRequest.h" +#include "HttpResponse.h" + +NS_CC_EXT_BEGIN + +/** + * @addtogroup Network + * @{ + */ + + +/** @brief Singleton that handles asynchrounous http requests + * Once the request completed, a callback will issued in main thread when it provided during make request + */ +class CC_DLL CCHttpClient : public cocos2d::CCObject +{ +public: + /** Return the shared instance **/ + static CCHttpClient *getInstance(); + + /** Relase the shared instance **/ + static void destroyInstance(); + + /** + * Add a get request to task queue + * @param request a HttpRequest object, which includes url, response callback etc. + * @return NULL + */ + void send(HttpRequest* request); + + + /** + * Add a download request to task queue + * @param url urls want to download + * @param pTarget callback target + * @param pSelector callback selector + * @return NULL + + void addDownloadTask(std::vector &urls, cocos2d::CCObject *pTarget, cocos2d::SEL_CallFuncND pSelector); + */ + + /** + * Change the connect timeout + * @param timeout + * @return NULL + */ + inline void setTimeoutForConnect(int value) {_timeoutForConnect = value;}; + + /** + * Get connect timeout + * @return int + * + */ + inline int getTimeoutForConnect() {return _timeoutForConnect;} + + + /** + * Change the download timeout + * @param timeout + * @return NULL + */ + inline void setTimeoutForRead(int value) {_timeoutForRead = value;}; + + + /** + * Get download timeout + * @return int + */ + inline int getTimeoutForRead() {return _timeoutForRead;}; + +private: + CCHttpClient(); + virtual ~CCHttpClient(); + bool init(void); + + /** + * Init pthread mutex, semaphore, and create new thread for http requests + * @return bool + */ + bool lazyInitThreadSemphore(); + /** Poll function called from main thread to dispatch callbacks when http requests finished **/ + void dispatchResponseCallbacks(float delta); + +private: + int _timeoutForConnect; + int _timeoutForRead; + + // std::string reqId; +}; + +// end of Network group +/// @} + +NS_CC_EXT_END + +#endif //__CCHTTPREQUEST_H__ diff --git a/extensions/Network/HttpRequest.h b/extensions/Network/HttpRequest.h new file mode 100644 index 0000000000..a1bcfbbcd2 --- /dev/null +++ b/extensions/Network/HttpRequest.h @@ -0,0 +1,175 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + + 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. + ****************************************************************************/ + +/* + //Http request type + typedef enum { + kHttpRequestGet, + kHttpRequestPost, + // kHttpRequestDownloadFile, + } HttpRequestType; + + /// Http Request structure + typedef struct { + HttpRequestType requestType; /// kHttpRequestGet, kHttpRequestPost or other enums + std::string url; /// target url that this request is sent to + std::vector requestData; /// used for POST + std::string tag; /// user defined tag, to identify different requests in response callback + cocos2d::CCObject* pTarget; /// callback target of pSelector function + cocos2d::SEL_CallFuncND pSelector; /// callback function, e.g. MyLayer::onHttpResponse(CCObject *sender, void *data) + void* pUserData; /// You can add your customed data here + } HttpRequestPacket; + */ + + +#ifndef __HTTP_REQUEST_H__ +#define __HTTP_REQUEST_H__ + +#include "cocos2d.h" +#include "ExtensionMacros.h" + +NS_CC_EXT_BEGIN + +class HttpRequest : public cocos2d::CCObject +{ +public: + /** Use this enum type as param in setReqeustType(param) */ + typedef enum + { + kHttpGet, + kHttpPost, + kHttpUnkown, + } HttpRequestType; + + HttpRequest() + { + _requestType = kHttpUnkown; + _url.clear(); + _requestData.clear(); + _tag.clear(); + _pTarget = NULL; + _pSelector = NULL; + _pUserData = NULL; + }; + + virtual ~HttpRequest() + { + if (_pTarget) + { + _pTarget->release(); + } + }; + + /** override autorelease method to avoid developers to call it */ + CCObject* autorelease(void) + { + CCAssert(true, "HttpResponse is used between network thread and ui thread \ + therefore, autorelease is forbidden here"); + return NULL; + } + + // setter/getters for properties + + inline void setRequestType(HttpRequestType type) + { + _requestType = type; + }; + inline HttpRequestType getRequestType() + { + return _requestType; + }; + + inline void setUrl(const char* url) + { + _url = url; + }; + inline const char* getUrl() + { + return _url.c_str(); + }; + + inline void setRequestData(const char* buffer, unsigned int len) + { + _requestData.assign(buffer, buffer + len); + }; + inline char* getRequestData() + { + return &(_requestData.front()); + } + inline int getRequestDataSize() + { + return _requestData.size(); + } + + inline void setTag(const char* tag) + { + _tag = tag; + }; + inline const char* getTag() + { + return _tag.c_str(); + }; + + inline void setUserData(void* pUserData) + { + _pUserData = pUserData; + }; + inline void* getUserData() + { + return _pUserData; + }; + + inline void setResponseCallback(cocos2d::CCObject* pTarget, cocos2d::SEL_CallFuncND pSelector) + { + _pTarget = pTarget; + _pSelector = pSelector; + + if (_pTarget) + { + _pTarget->retain(); + } + } + inline CCObject* getTarget() + { + return _pTarget; + } + inline cocos2d::SEL_CallFuncND getSelector() + { + return _pSelector; + } + +protected: + // properties + HttpRequestType _requestType; /// kHttpRequestGet, kHttpRequestPost or other enums + std::string _url; /// target url that this request is sent to + std::vector _requestData; /// used for POST + std::string _tag; /// user defined tag, to identify different requests in response callback + cocos2d::CCObject* _pTarget; /// callback target of pSelector function + cocos2d::SEL_CallFuncND _pSelector; /// callback function, e.g. MyLayer::onHttpResponse(CCObject *sender, void *data) + void* _pUserData; /// You can add your customed data here +}; + +NS_CC_EXT_END + +#endif //__HTTP_REQUEST_H__ \ No newline at end of file diff --git a/extensions/Network/HttpResponse.h b/extensions/Network/HttpResponse.h new file mode 100644 index 0000000000..b60d5e0529 --- /dev/null +++ b/extensions/Network/HttpResponse.h @@ -0,0 +1,121 @@ +/**************************************************************************** + Copyright (c) 2010-2012 cocos2d-x.org + + 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. + ****************************************************************************/ + +#ifndef __HTTP_RESPONSE__ +#define __HTTP_RESPONSE__ + +#include "cocos2d.h" +#include "ExtensionMacros.h" +#include "HttpRequest.h" + +NS_CC_EXT_BEGIN + +class HttpResponse : public cocos2d::CCObject +{ +public: + HttpResponse(HttpRequest* request) + { + _pHttpRequest = request; + if (_pHttpRequest) + { + _pHttpRequest->retain(); + } + + _succeed = false; + _responseData.clear(); + _errorBuffer.clear(); + } + virtual ~HttpResponse() + { + if (_pHttpRequest) + { + _pHttpRequest->release(); + } + } + + /** override autorelease method to prevent developers from calling it */ + CCObject* autorelease(void) + { + CCAssert(true, "HttpResponse is used between network thread and ui thread \ + therefore, autorelease is forbidden here"); + return NULL; + } + + // setters,getters for properties + inline HttpRequest* getHttpRequest() + { + return _pHttpRequest; + } + + inline void setSucceed(bool value) + { + _succeed = value; + }; + inline bool isSucceed() + { + return _succeed; + }; + + inline void setResponseData(std::vector* data) + { + _responseData = *data; + } + inline std::vector* getResponseData() + { + return &_responseData; + } + + inline void setResponseCode(int value) + { + _responseCode = value; + } + inline int getResponseCode() + { + return _responseCode; + } + + inline void setErrorBuffer(const char* value) + { + _errorBuffer.clear(); + _errorBuffer.assign(value); + }; + inline const char* getErrorBuffer() + { + return _errorBuffer.c_str(); + } + +protected: + bool initWithRequest(HttpRequest* request); + + // properties + HttpRequest* _pHttpRequest; /// the corresponding HttpRequest pointer who leads to this response + bool _succeed; + std::vector _responseData; + int _responseCode; /// the CURLcode returned from libcurl + std::string _errorBuffer; /// if _responseCode != 200, please read _errorBuffer to find the reason +}; + +NS_CC_EXT_END + +#endif //__HTTP_RESPONSE_H__ diff --git a/extensions/cocos-ext.h b/extensions/cocos-ext.h index 6c6db6fd2d..cd7fec1868 100644 --- a/extensions/cocos-ext.h +++ b/extensions/cocos-ext.h @@ -28,7 +28,9 @@ #include "GUI/CCControlExtension/CCControlExtensions.h" #include "GUI/CCScrollView/CCScrollView.h" -#include "Network/CCHttpRequest/CCHttpRequest.h" +#include "network/HttpRequest.h" +#include "network/HttpResponse.h" +#include "network/HttpClient.h" #endif /* __COCOS2D_EXT_H__ */ diff --git a/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp index 18474386e0..f55bbd5b4e 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp @@ -3,7 +3,7 @@ #include "NotificationCenterTest/NotificationCenterTest.h" #include "ControlExtensionTest/CCControlSceneManager.h" #include "CocosBuilderTest/CocosBuilderTest.h" -#include "HttpRequestTest/HttpRequestTest.h" +#include "NetworkTest/HttpRequestTest.h" enum { diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp similarity index 62% rename from samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp rename to samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp index c1d5f49a7a..53374bf851 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp @@ -38,12 +38,6 @@ HttpRequestTest::HttpRequestTest() : m_labelStatusCode(NULL) itemPostBinary->setPosition(ccp(winSize.width / 2, winSize.height - 160)); menuRequest->addChild(itemPostBinary); - //Download File - CCLabelTTF *labelDownload = CCLabelTTF::create("Test Download", "Arial", 15); - CCMenuItemLabel *itemDownload = CCMenuItemLabel::create(labelDownload, this, menu_selector(HttpRequestTest::onLabelDownloadTestClicked)); - itemDownload->setPosition(ccp(winSize.width / 2, winSize.height - 190)); - menuRequest->addChild(itemDownload); - //Response Code Label CCLabelTTF *labelStatus = CCLabelTTF::create("Notice: Replace Post Url With Your Own", "Marker Felt", 20); labelStatus->setPosition(ccp(winSize.width / 2, winSize.height - 250)); @@ -59,26 +53,65 @@ HttpRequestTest::HttpRequestTest() : m_labelStatusCode(NULL) } void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender) -{ - string url = GETURL; +{ + /***** + test 1 + ******/ - CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); - requestor->addGetTask(url, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + HttpRequest* request1 = new HttpRequest(); + // required fields + request1->setUrl("http://www.google.com"); + request1->setRequestType(HttpRequest::kHttpGet); + request1->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + // optional fields + request1->setTag("HttpTest: GET"); + + CCHttpClient::getInstance()->send(request1); + + // don't forget to release it, pair to new + request1->release(); + + /***** + test 2 + ******/ + HttpRequest* request2 = new HttpRequest(); + request2->setUrl("http://www.baidu.com"); + request2->setRequestType(HttpRequest::kHttpGet); + request2->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + CCHttpClient::getInstance()->send(request2); + request2->release(); + + /***** + test 3 + ******/ + HttpRequest* request3 = new HttpRequest(); + request3->setUrl("http://just-make-this-request-failed.com"); + request3->setRequestType(HttpRequest::kHttpGet); + request3->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + CCHttpClient::getInstance()->send(request3); + request3->release(); + } void HttpRequestTest::onLabelPostTestClicked(cocos2d::CCObject *sender) { - string url = POSTURL; - string content = "username=hello&password=world"; + const char* content = "username=a\0b"; - CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); - //You may name the request - requestor->setReqId("login"); - requestor->addPostTask(url, content, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + HttpRequest* request = new HttpRequest(); + request->autorelease(); + + request->setUrl(POSTURL); + request->setRequestData(content, strlen(content) + 2); + request->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + + request->setTag("HttpTest: POST"); + + CCHttpClient::getInstance()->send(request); } void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender) { +/* string url = POSTURL; const char *content = "username=a\0b"; @@ -91,45 +124,56 @@ void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender) vec.insert(vec.end(), content, content + strlen(content) + 2); requestor->setReqId("postbinary"); requestor->addPostTask(url, vec, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); -} - -void HttpRequestTest::onLabelDownloadTestClicked(cocos2d::CCObject *sender) -{ - string url = DOWNLOADURL; - vector files; - - files.push_back(url); - - CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); - requestor->setReqId("download"); - requestor->addDownloadTask(files, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + */ } void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *data) { - HttpResponsePacket *response = (HttpResponsePacket *)data; - - //You can get original request type from: response->request->reqType - - if (response->request->reqId != "") { - CCLog("%s completed", response->request->reqId.c_str()); - } - - char buffer[128]; - sprintf(buffer, "Response code: %d", response->responseCode); - getLabelStatusCode()->setString(buffer); - - if (!response->succeed) { + HttpResponse *response = (HttpResponse*)data; + + if (!response) + { return; } + // You can get original request type from: response->request->reqType + if (0 != strlen(response->getHttpRequest()->getTag())) + { + CCLog("%s completed", response->getHttpRequest()->getTag()); + } + + CCLog("response code: %d", response->getResponseCode()); + + if (!response->isSucceed()) + { + CCLog("response failed"); + CCLog("error buffer: %s", response->getErrorBuffer()); + return; + } + + std::vector *buffer = response->getResponseData(); + printf("Http Test, dump data: "); + for (int i = 0; i < buffer->size(); i++) + { + printf("%c", (*buffer)[i]); + } + printf("\n"); + + // dump response data + + //If the response is binary, use response->responseData.data() and response->responseData.length() //To process the response + + /* if (response->responseData.length() >= kMaxLogLen) { response->responseData = response->responseData.substr(0, kMaxLogLen / 2); } - CCLog("Response Content: %s", response->responseData.c_str()); + */ + // CCLog("Response Content: %s", response->responseData.begin()); + + /* if (response->request->reqId == "postbinary") { int32_t length = response->responseData.length(); const char *data = response->responseData.data(); @@ -138,13 +182,8 @@ void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *da CCLog("%c", data[i]); } } + */ - if (response->request->reqId == "download") { - string downloaded = response->request->files[0]; - string saved = downloaded.substr(downloaded.rfind("/") + 1); - - CCLog("%s downloaded, and saved as %s in your application's writeable directory", downloaded.c_str(), saved.c_str()); - } } void HttpRequestTest::toExtensionsMainLayer(cocos2d::CCObject *sender) diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.h similarity index 100% rename from samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h rename to samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.h diff --git a/samples/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id b/samples/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id index aafb08ecb8..72d497708a 100644 --- a/samples/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id +++ b/samples/TestCpp/proj.ios/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id @@ -1 +1 @@ -eae8f176936098bc77ca4472b94f9e018f5c368c \ No newline at end of file +afa666f94104aca6725fb837f7c037ba586c0fbd \ No newline at end of file