From dc35596ce91a718886cf057f63e6f38d110c60c4 Mon Sep 17 00:00:00 2001 From: qingyun Date: Sat, 4 Aug 2012 18:12:39 +0800 Subject: [PATCH 01/11] add greathqy to AUTHORS --- AUTHORS | 3 +++ .../HttpRequestTest/HttpRequestTest.cpp | 9 +++++++++ .../HttpRequestTest/HttpRequestTest.h | 14 ++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp create mode 100644 samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h diff --git a/AUTHORS b/AUTHORS index 5bc5ee9158..bcb9d1222e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -93,6 +93,9 @@ Developers: Nat Weiss Minor enhancements to the Cocos2D-X codebase and Android build scripts + Qingyun Huang (greathqy) + implement CCHttpRequest + Retired Core Developers: WenSheng Yang Author of windows port, CCTextField, diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp new file mode 100644 index 0000000000..51f0d4c335 --- /dev/null +++ b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp @@ -0,0 +1,9 @@ +// +// HttpRequestTest.cpp +// TestCpp +// +// Created by qingyun on 12-8-4. +// Copyright (c) 2012年 __MyCompanyName__. All rights reserved. +// + +#include diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h new file mode 100644 index 0000000000..dd19b4f298 --- /dev/null +++ b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h @@ -0,0 +1,14 @@ +// +// HttpRequestTest.h +// TestCpp +// +// Created by qingyun on 12-8-4. +// Copyright (c) 2012年 __MyCompanyName__. All rights reserved. +// + +#ifndef TestCpp_HttpRequestTest_h +#define TestCpp_HttpRequestTest_h + + + +#endif From 24136dc57593ae8489cd0ad4e8a928b6f5c9adfd Mon Sep 17 00:00:00 2001 From: qingyun Date: Sat, 4 Aug 2012 18:27:27 +0800 Subject: [PATCH 02/11] add CCHttpRequest and related tests to repository --- .../Network/CCHttpRequest/CCHttpRequest.cpp | 576 ++++++++++++++++++ .../Network/CCHttpRequest/CCHttpRequest.h | 185 ++++++ extensions/cocos-ext.h | 2 + .../Classes/ExtensionsTest/ExtensionsTest.cpp | 7 + .../HttpRequestTest/HttpRequestTest.cpp | 155 ++++- .../HttpRequestTest/HttpRequestTest.h | 32 +- .../project.pbxproj.REMOVED.git-id | 2 +- 7 files changed, 940 insertions(+), 19 deletions(-) create mode 100644 extensions/Network/CCHttpRequest/CCHttpRequest.cpp create mode 100644 extensions/Network/CCHttpRequest/CCHttpRequest.h diff --git a/extensions/Network/CCHttpRequest/CCHttpRequest.cpp b/extensions/Network/CCHttpRequest/CCHttpRequest.cpp new file mode 100644 index 0000000000..af2ec811ed --- /dev/null +++ b/extensions/Network/CCHttpRequest/CCHttpRequest.cpp @@ -0,0 +1,576 @@ +/**************************************************************************** + 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 new file mode 100644 index 0000000000..01d55e6bb3 --- /dev/null +++ b/extensions/Network/CCHttpRequest/CCHttpRequest.h @@ -0,0 +1,185 @@ +/**************************************************************************** + 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 + +//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; +}; + +NS_CC_EXT_END + +#endif //__CCHTTPREQUEST_H__ diff --git a/extensions/cocos-ext.h b/extensions/cocos-ext.h index 4b9f2e4e84..6c6db6fd2d 100644 --- a/extensions/cocos-ext.h +++ b/extensions/cocos-ext.h @@ -28,5 +28,7 @@ #include "GUI/CCControlExtension/CCControlExtensions.h" #include "GUI/CCScrollView/CCScrollView.h" +#include "Network/CCHttpRequest/CCHttpRequest.h" + #endif /* __COCOS2D_EXT_H__ */ diff --git a/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp index 018fefb1d2..18474386e0 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp @@ -3,6 +3,7 @@ #include "NotificationCenterTest/NotificationCenterTest.h" #include "ControlExtensionTest/CCControlSceneManager.h" #include "CocosBuilderTest/CocosBuilderTest.h" +#include "HttpRequestTest/HttpRequestTest.h" enum { @@ -15,6 +16,7 @@ enum TEST_NOTIFICATIONCENTER = 0, TEST_CCCONTROLBUTTON, TEST_COCOSBUILDER, + TEST_HTTPREQUEST, TEST_MAX_COUNT, }; @@ -23,6 +25,7 @@ static const std::string testsName[TEST_MAX_COUNT] = "NotificationCenterTest", "CCControlButtonTest", "CocosBuilderTest", + "HttpRequestTest", }; //////////////////////////////////////////////////////// @@ -80,6 +83,10 @@ void ExtensionsMainLayer::menuCallback(CCObject* pSender) } } break; + case TEST_HTTPREQUEST: + { + runHttpRequestTest(); + } default: break; } diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp index 51f0d4c335..a264e387e8 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp @@ -1,9 +1,148 @@ -// -// HttpRequestTest.cpp -// TestCpp -// -// Created by qingyun on 12-8-4. -// Copyright (c) 2012年 __MyCompanyName__. All rights reserved. -// +#include "HttpRequestTest.h" +#include "../ExtensionsTest.h" + +#define GETURL "http://www.google.com" +#define POSTURL "http://www.replacewithyours.com/post.php" +#define DOWNLOADURL "http://www.google.com/index.html" + +USING_NS_CC; +USING_NS_CC_EXT; + +HttpRequestTest::HttpRequestTest() : m_labelStatusCode(NULL) +{ + CCSize winSize = CCDirector::sharedDirector()->getWinSize(); + + CCLabelTTF *label = CCLabelTTF::create("Http Request Test", "Arial", 28); + label->setPosition(ccp(winSize.width / 2, winSize.height - 50)); + addChild(label, 0); + + CCMenu *menuRequest = CCMenu::create(); + menuRequest->setPosition(CCPointZero); + addChild(menuRequest); + + //Get + CCLabelTTF *labelGet = CCLabelTTF::create("Test Get", "Arial", 15); + CCMenuItemLabel *itemGet = CCMenuItemLabel::create(labelGet, this, menu_selector(HttpRequestTest::onLabelGetTestClicked)); + itemGet->setPosition(ccp(winSize.width / 2, winSize.height - 100)); + menuRequest->addChild(itemGet); + + //Post + CCLabelTTF *labelPost = CCLabelTTF::create("Test Post", "Arial", 15); + CCMenuItemLabel *itemPost = CCMenuItemLabel::create(labelPost, this, menu_selector(HttpRequestTest::onLabelPostTestClicked)); + itemPost->setPosition(ccp(winSize.width / 2, winSize.height - 130)); + menuRequest->addChild(itemPost); + + //Post Binary + CCLabelTTF *labelPostBinary = CCLabelTTF::create("Test Post Binary", "Arial", 15); + CCMenuItemLabel *itemPostBinary = CCMenuItemLabel::create(labelPostBinary, this, menu_selector(HttpRequestTest::onLabelPostBinaryTestClicked)); + 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)); + addChild(labelStatus); + setLabelStatusCode(labelStatus); + + //Back Menu + CCMenuItemFont *itemBack = CCMenuItemFont::create("Back", this, menu_selector(HttpRequestTest::toExtensionsMainLayer)); + itemBack->setPosition(ccp(winSize.width - 50, 25)); + CCMenu *menuBack = CCMenu::create(itemBack, NULL); + menuBack->setPosition(CCPointZero); + addChild(menuBack); +} + +void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender) +{ + string url = GETURL; + + CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); + requestor->addGetTask(url, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); +} + +void HttpRequestTest::onLabelPostTestClicked(cocos2d::CCObject *sender) +{ + string url = POSTURL; + string content = "username=hello&password=world"; + + CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); + //You may name the request + requestor->setReqId("login"); + requestor->addPostTask(url, content, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); +} + +void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender) +{ + string url = POSTURL; + const char *content = "username=hello&password=worl\0d"; + + CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); + requestor->setReqId("postbinary"); + requestor->addPostTask(url, content, strlen(content) + 2, 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) { + return; + } + + //If the response is binary, use response->responseData.data() and response->responseData.length() + //To process the response + CCLog("Response Content: %s", response->responseData.c_str()); + + 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) +{ + ExtensionsTestScene *pScene = new ExtensionsTestScene(); + pScene->runThisTest(); + pScene->release(); +} + +void runHttpRequestTest() +{ + CCScene *pScene = CCScene::create(); + HttpRequestTest *pLayer = new HttpRequestTest(); + pScene->addChild(pLayer); + + CCDirector::sharedDirector()->replaceScene(pScene); + pLayer->release(); +} -#include diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h index dd19b4f298..bb1a387354 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h +++ b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.h @@ -1,14 +1,26 @@ -// -// HttpRequestTest.h -// TestCpp -// -// Created by qingyun on 12-8-4. -// Copyright (c) 2012年 __MyCompanyName__. All rights reserved. -// +#ifndef __HTTPREQUESTHTTP_H +#define __HTTPREQUESTHTTP_H -#ifndef TestCpp_HttpRequestTest_h -#define TestCpp_HttpRequestTest_h +#include "cocos2d.h" +#include "cocos-ext.h" +class HttpRequestTest : public cocos2d::CCLayer +{ + CC_SYNTHESIZE(CCLabelTTF *, m_labelStatusCode, LabelStatusCode); +public: + HttpRequestTest(); + void toExtensionsMainLayer(cocos2d::CCObject *sender); + + //Menu Callbacks + void onLabelGetTestClicked(cocos2d::CCObject *sender); + void onLabelPostTestClicked(cocos2d::CCObject *sender); + void onLabelPostBinaryTestClicked(cocos2d::CCObject *sender); + void onLabelDownloadTestClicked(cocos2d::CCObject *sender); + + //Http Response Callback + void onHttpRequestCompleted(cocos2d::CCObject *sender, void *data); +}; +void runHttpRequestTest(); -#endif +#endif //__HTTPREQUESTHTTP_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 d64a5dc114..aafb08ecb8 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 @@ -7dea09f758e229595c7992fee3cc0a0f0fb49f79 \ No newline at end of file +eae8f176936098bc77ca4472b94f9e018f5c368c \ No newline at end of file From b3206737e36af625e1691e95097ac96adc801c14 Mon Sep 17 00:00:00 2001 From: qingyun Date: Sat, 4 Aug 2012 18:30:05 +0800 Subject: [PATCH 03/11] add group for CCHttpRequest.h --- extensions/Network/CCHttpRequest/CCHttpRequest.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/Network/CCHttpRequest/CCHttpRequest.h b/extensions/Network/CCHttpRequest/CCHttpRequest.h index 01d55e6bb3..b9fd5bd66e 100644 --- a/extensions/Network/CCHttpRequest/CCHttpRequest.h +++ b/extensions/Network/CCHttpRequest/CCHttpRequest.h @@ -31,6 +31,11 @@ NS_CC_EXT_BEGIN +/** + * @addtogroup Network + * @{ + */ + //Http request type typedef enum { kHttpRequestGet, @@ -180,6 +185,9 @@ private: std::string reqId; }; +// end of Network group +/// @} + NS_CC_EXT_END #endif //__CCHTTPREQUEST_H__ From 054fe4993c9b1d1f47a2e0775ba3913798187cab Mon Sep 17 00:00:00 2001 From: qingyun Date: Mon, 6 Aug 2012 14:47:23 +0800 Subject: [PATCH 04/11] fix CCLog stack overflow --- .../HttpRequestTest/HttpRequestTest.cpp | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp index a264e387e8..c1d5f49a7a 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/HttpRequestTest/HttpRequestTest.cpp @@ -80,11 +80,17 @@ void HttpRequestTest::onLabelPostTestClicked(cocos2d::CCObject *sender) void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender) { string url = POSTURL; - const char *content = "username=hello&password=worl\0d"; + const char *content = "username=a\0b"; + CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); requestor->setReqId("postbinary"); requestor->addPostTask(url, content, strlen(content) + 2, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + + std::vector vec; + 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) @@ -119,8 +125,20 @@ void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *da //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()); + if (response->request->reqId == "postbinary") { + int32_t length = response->responseData.length(); + const char *data = response->responseData.data(); + + for (int32_t i = 0; i < length; ++i) { + CCLog("%c", data[i]); + } + } + if (response->request->reqId == "download") { string downloaded = response->request->files[0]; string saved = downloaded.substr(downloaded.rfind("/") + 1); From b094f70ab314984a14b5787fc660ea68e500bc30 Mon Sep 17 00:00:00 2001 From: Walzer Date: Tue, 7 Aug 2012 18:28:07 +0800 Subject: [PATCH 05/11] 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 From 9ee50bb59aeb000fc804a67a246dc1212e2fe697 Mon Sep 17 00:00:00 2001 From: Walzer Date: Wed, 8 Aug 2012 12:05:02 +0800 Subject: [PATCH 06/11] issue #1424, add doxygen comments, use httpbin.org interfaces in GET/POST before setting up our website. --- extensions/Network/HttpClient.cpp | 4 +- extensions/Network/HttpClient.h | 10 -- extensions/Network/HttpRequest.h | 67 ++++++++----- extensions/Network/HttpResponse.h | 97 ++++++++++++++----- .../NetworkTest/HttpRequestTest.cpp | 67 ++++--------- 5 files changed, 138 insertions(+), 107 deletions(-) diff --git a/extensions/Network/HttpClient.cpp b/extensions/Network/HttpClient.cpp index 56152fa80e..9e78b55348 100644 --- a/extensions/Network/HttpClient.cpp +++ b/extensions/Network/HttpClient.cpp @@ -84,7 +84,7 @@ int processPostTask(HttpRequest *task, write_callback callback, void *stream, in // int processDownloadTask(HttpRequest *task, write_callback callback, void *stream, int32_t *errorCode); -//Worker thread +// Worker thread static void* networkThread(void *data) { HttpRequest *request = NULL; @@ -149,6 +149,7 @@ static void* networkThread(void *data) break; } + // write data to HttpResponse response->setResponseCode(responseCode); if (retValue != 0) @@ -161,6 +162,7 @@ static void* networkThread(void *data) response->setSucceed(true); } + // add response packet into queue pthread_mutex_lock(&s_responseQueueMutex); s_responseQueue->addObject(response); diff --git a/extensions/Network/HttpClient.h b/extensions/Network/HttpClient.h index f78f5d0407..db7bc4f32b 100644 --- a/extensions/Network/HttpClient.h +++ b/extensions/Network/HttpClient.h @@ -59,16 +59,6 @@ public: */ 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 diff --git a/extensions/Network/HttpRequest.h b/extensions/Network/HttpRequest.h index a1bcfbbcd2..0bc101f3ab 100644 --- a/extensions/Network/HttpRequest.h +++ b/extensions/Network/HttpRequest.h @@ -22,27 +22,6 @@ 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__ @@ -51,6 +30,11 @@ NS_CC_EXT_BEGIN +/** + @brief defines the object which users must packed for CCHttpClient::send(HttpRequest*) method. + Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpRequestTest.cpp as a sample + @since v2.0.2 + */ class HttpRequest : public cocos2d::CCObject { public: @@ -62,6 +46,12 @@ public: kHttpUnkown, } HttpRequestType; + /** Constructor + Because HttpRequest object will be used between UI thead and network thread, + requestObj->autorelease() is forbidden to avoid crashes in CCAutoreleasePool + new/retain/release still works, which means you need to release it manually + Please refer to HttpRequestTest.cpp to find its usage + */ HttpRequest() { _requestType = kHttpUnkown; @@ -73,6 +63,7 @@ public: _pUserData = NULL; }; + /** Destructor */ virtual ~HttpRequest() { if (_pTarget) @@ -81,65 +72,89 @@ public: } }; - /** override autorelease method to avoid developers to call it */ + /** Override autorelease method to avoid developers to call it */ CCObject* autorelease(void) { - CCAssert(true, "HttpResponse is used between network thread and ui thread \ + CCAssert(false, "HttpResponse is used between network thread and ui thread \ therefore, autorelease is forbidden here"); return NULL; } // setter/getters for properties - + + /** Required field for HttpRequest object before being sent. + kHttpGet & kHttpPost is currently supported + */ inline void setRequestType(HttpRequestType type) { _requestType = type; }; + /** Get back the kHttpGet/Post/... enum value */ inline HttpRequestType getRequestType() { return _requestType; }; + /** Required field for HttpRequest object before being sent. + */ inline void setUrl(const char* url) { _url = url; }; + /** Get back the setted url */ inline const char* getUrl() { return _url.c_str(); }; + /** Option field. You can set your post data here + */ inline void setRequestData(const char* buffer, unsigned int len) { _requestData.assign(buffer, buffer + len); }; + /** Get the request data pointer back */ inline char* getRequestData() { return &(_requestData.front()); } + /** Get the size of request data back */ inline int getRequestDataSize() { return _requestData.size(); } + /** Option field. You can set a string tag to identify your request, this tag can be found in HttpResponse->getHttpRequest->getTag() + */ inline void setTag(const char* tag) { _tag = tag; }; + /** Get the string tag back to identify the request. + The best practice is to use it in your MyClass::onMyHttpRequestCompleted(sender, HttpResponse*) callback + */ inline const char* getTag() { return _tag.c_str(); }; + /** Option field. You can attach a customed data in each request, and get it back in response callback. + But you need to new/delete the data pointer manully + */ inline void setUserData(void* pUserData) { _pUserData = pUserData; }; + /** Get the pre-setted custom data pointer back. + Don't forget to delete it. HttpClient/HttpResponse/HttpRequest will do nothing with this pointer + */ inline void* getUserData() { return _pUserData; }; + /** Required field. You should set the callback selector function at ack the http request completed + */ inline void setResponseCallback(cocos2d::CCObject* pTarget, cocos2d::SEL_CallFuncND pSelector) { _pTarget = pTarget; @@ -149,11 +164,13 @@ public: { _pTarget->retain(); } - } + } + /** Get the target of callback selector funtion, mainly used by CCHttpClient */ inline CCObject* getTarget() { return _pTarget; } + /** Get the selector function pointer, mainly used by CCHttpClient */ inline cocos2d::SEL_CallFuncND getSelector() { return _pSelector; diff --git a/extensions/Network/HttpResponse.h b/extensions/Network/HttpResponse.h index b60d5e0529..35f426b481 100644 --- a/extensions/Network/HttpResponse.h +++ b/extensions/Network/HttpResponse.h @@ -31,9 +31,17 @@ NS_CC_EXT_BEGIN +/** + @brief defines the object which users will receive at onHttpCompleted(sender, HttpResponse) callback + Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpRequestTest.cpp as a sample + @since v2.0.2 + */ class HttpResponse : public cocos2d::CCObject { public: + /** Constructor, it's used by CCHttpClient internal, users don't need to create HttpResponse manually + @param request the corresponding HttpRequest which leads to this response + */ HttpResponse(HttpRequest* request) { _pHttpRequest = request; @@ -46,6 +54,10 @@ public: _responseData.clear(); _errorBuffer.clear(); } + + /** Destructor, it will be called in CCHttpClient internal, + users don't need to desturct HttpResponse object manully + */ virtual ~HttpResponse() { if (_pHttpRequest) @@ -54,66 +66,103 @@ public: } } - /** override autorelease method to prevent developers from calling it */ + /** Override autorelease method to prevent developers from calling it */ CCObject* autorelease(void) { - CCAssert(true, "HttpResponse is used between network thread and ui thread \ + CCAssert(false, "HttpResponse is used between network thread and ui thread \ therefore, autorelease is forbidden here"); return NULL; } - // setters,getters for properties + // getters, will be called by users + + /** Get the corresponding HttpRequest object which leads to this response + There's no paired setter for it, coz it's already setted in class constructor + */ inline HttpRequest* getHttpRequest() { return _pHttpRequest; } - - inline void setSucceed(bool value) - { - _succeed = value; - }; + + /** To see if the http reqeust is returned successfully, + Althrough users can judge if (http return code = 200), we want an easier way + If this getter returns false, you can call getResponseCode and getErrorBuffer to find more details + */ inline bool isSucceed() { return _succeed; }; - - inline void setResponseData(std::vector* data) - { - _responseData = *data; - } + + /** Get the http response raw data */ inline std::vector* getResponseData() { return &_responseData; } - - inline void setResponseCode(int value) - { - _responseCode = value; - } + + /** Get the http response errorCode + * I know that you want to see http 200 :) + */ inline int getResponseCode() { return _responseCode; } + + /** Get the rror buffer which will tell you more about the reason why http request failed + */ + inline const char* getErrorBuffer() + { + return _errorBuffer.c_str(); + } + // setters, will be called by CCHttpClient + // users should avoid invoking these methods + + + /** Set if the http request is returned successfully, + Althrough users can judge if (http code == 200), we want a easier way + This setter is mainly used in CCHttpClient, users mustn't set it directly + */ + inline void setSucceed(bool value) + { + _succeed = value; + }; + + + /** Set the http response raw buffer, is used by CCHttpClient + */ + inline void setResponseData(std::vector* data) + { + _responseData = *data; + } + + + /** Set the http response errorCode + */ + inline void setResponseCode(int value) + { + _responseCode = value; + } + + + /** Set the error buffer which will tell you more the reason why http request failed + */ 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; + bool _succeed; /// to indecate if the http reqeust is successful simply + std::vector _responseData; /// the returned raw data. You can also dump it as a string int _responseCode; /// the CURLcode returned from libcurl std::string _errorBuffer; /// if _responseCode != 200, please read _errorBuffer to find the reason + }; NS_CC_EXT_END diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp index 53374bf851..80cb18326e 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp @@ -60,7 +60,7 @@ void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender) HttpRequest* request1 = new HttpRequest(); // required fields - request1->setUrl("http://www.google.com"); + request1->setUrl("http://www.httpbin.org/ip"); request1->setRequestType(HttpRequest::kHttpGet); request1->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); // optional fields @@ -75,7 +75,7 @@ void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender) test 2 ******/ HttpRequest* request2 = new HttpRequest(); - request2->setUrl("http://www.baidu.com"); + request2->setUrl("http://www.httpbin.org/get"); request2->setRequestType(HttpRequest::kHttpGet); request2->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); CCHttpClient::getInstance()->send(request2); @@ -95,36 +95,34 @@ void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender) void HttpRequestTest::onLabelPostTestClicked(cocos2d::CCObject *sender) { - const char* content = "username=a\0b"; - HttpRequest* request = new HttpRequest(); - request->autorelease(); - - request->setUrl(POSTURL); - request->setRequestData(content, strlen(content) + 2); + request->setUrl("http://www.httpbin.org/post"); + request->setRequestType(HttpRequest::kHttpPost); request->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); - request->setTag("HttpTest: POST"); + // write the post data + const char* postData = "visitor=cocos2d&TestSuite=Extensions Test/NetowrkTest"; + request->setRequestData(postData, strlen(postData)); + request->setTag("HttpRequestTest: POST JSON"); CCHttpClient::getInstance()->send(request); + request->release(); } void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender) { -/* - string url = POSTURL; - const char *content = "username=a\0b"; + HttpRequest* request = new HttpRequest(); + request->setUrl("http://www.httpbin.org/post"); + request->setRequestType(HttpRequest::kHttpPost); + request->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); - - CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest(); - requestor->setReqId("postbinary"); - requestor->addPostTask(url, content, strlen(content) + 2, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); + // write the post data + char postData[22] = "binary=hello\0\0cocos2d"; // including \0, the strings after \0 should not be cut in response + request->setRequestData(postData, 22); - std::vector vec; - vec.insert(vec.end(), content, content + strlen(content) + 2); - requestor->setReqId("postbinary"); - requestor->addPostTask(url, vec, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); - */ + request->setTag("HttpRequestTest: POST Binary"); + CCHttpClient::getInstance()->send(request); + request->release(); } void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *data) @@ -151,6 +149,7 @@ void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *da return; } + // dump data std::vector *buffer = response->getResponseData(); printf("Http Test, dump data: "); for (int i = 0; i < buffer->size(); i++) @@ -158,32 +157,6 @@ void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *da 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.begin()); - - /* - if (response->request->reqId == "postbinary") { - int32_t length = response->responseData.length(); - const char *data = response->responseData.data(); - - for (int32_t i = 0; i < length; ++i) { - CCLog("%c", data[i]); - } - } - */ - } void HttpRequestTest::toExtensionsMainLayer(cocos2d::CCObject *sender) From 08198c082efdf588f2148e537b7575ca475e425b Mon Sep 17 00:00:00 2001 From: Walzer Date: Thu, 9 Aug 2012 10:45:11 +0800 Subject: [PATCH 07/11] issue #1424, http wrapper has been tested on iOS, mac, android port. --- cocos2dx/Android.mk | 2 - cocos2dx/actions/CCActionManager.cpp | 2 +- .../android/prebuilt/libcurl/Android.mk | 2 +- cocos2dx/proj.linux/Makefile | 3 + extensions/Android.mk | 11 +- extensions/Network/HttpClient.cpp | 57 +++-- extensions/Network/HttpClient.h | 4 +- extensions/Network/HttpRequest.h | 8 +- extensions/Network/HttpResponse.h | 17 +- samples/TestCpp/Android.mk | 4 +- .../Classes/ExtensionsTest/ExtensionsTest.cpp | 10 +- .../NetworkTest/HttpClientTest.cpp | 194 ++++++++++++++++++ .../NetworkTest/HttpClientTest.h | 28 +++ .../NetworkTest/HttpRequestTest.cpp | 178 ---------------- .../NetworkTest/HttpRequestTest.h | 26 --- .../project.pbxproj.REMOVED.git-id | 2 +- samples/TestCpp/proj.linux/Makefile | 2 + .../project.pbxproj.REMOVED.git-id | 2 +- 18 files changed, 284 insertions(+), 268 deletions(-) create mode 100644 samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp create mode 100644 samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h delete mode 100644 samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp delete mode 100644 samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.h diff --git a/cocos2dx/Android.mk b/cocos2dx/Android.mk index 29bde858d6..543985d848 100644 --- a/cocos2dx/Android.mk +++ b/cocos2dx/Android.mk @@ -154,7 +154,6 @@ LOCAL_WHOLE_STATIC_LIBRARIES += cocos_jpeg_static LOCAL_WHOLE_STATIC_LIBRARIES += cocos_libxml2_static LOCAL_WHOLE_STATIC_LIBRARIES += cocos_libtiff_static - # define the macro to compile through support/zip_support/ioapi.c LOCAL_CFLAGS := -DUSE_FILE32API @@ -164,4 +163,3 @@ $(call import-module,libjpeg) $(call import-module,libpng) $(call import-module,libxml2) $(call import-module,libtiff) - diff --git a/cocos2dx/actions/CCActionManager.cpp b/cocos2dx/actions/CCActionManager.cpp index 63b0b7a119..5fd2514a75 100644 --- a/cocos2dx/actions/CCActionManager.cpp +++ b/cocos2dx/actions/CCActionManager.cpp @@ -317,7 +317,7 @@ CCAction* CCActionManager::getActionByTag(unsigned int tag, CCObject *pTarget) } else { - CCLOG("cocos2d : getActionByTag: Target not found"); + // CCLOG("cocos2d : getActionByTag: Target not found"); } return NULL; diff --git a/cocos2dx/platform/third_party/android/prebuilt/libcurl/Android.mk b/cocos2dx/platform/third_party/android/prebuilt/libcurl/Android.mk index 7b12cdbb93..8d0c18d79f 100644 --- a/cocos2dx/platform/third_party/android/prebuilt/libcurl/Android.mk +++ b/cocos2dx/platform/third_party/android/prebuilt/libcurl/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE := curl_static_prebuilt +LOCAL_MODULE := cocos_curl_static LOCAL_MODULE_FILENAME := curl LOCAL_SRC_FILES := libs/$(TARGET_ARCH_ABI)/libcurl.a LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include diff --git a/cocos2dx/proj.linux/Makefile b/cocos2dx/proj.linux/Makefile index ac509397c9..a8c503bc4a 100644 --- a/cocos2dx/proj.linux/Makefile +++ b/cocos2dx/proj.linux/Makefile @@ -17,12 +17,14 @@ INCLUDES = -I.. \ -I../../extensions/CCBReader \ -I../../extensions/GUI/CCControlExtension \ -I../../extensions/GUI/CCControlExtension \ + -I../../extensions/network \ -I../platform/linux/ \ -I../platform/third_party/linux/glew-1.7.0/glew-1.7.0/include/ \ -I../platform/third_party/linux/libxml2 \ -I../platform/third_party/linux/libpng \ -I../platform/third_party/linux/libjpeg \ -I../platform/third_party/linux/libtiff/include \ + -I../platform/third_party/linux/curl/include \ # -I../platform/third_party/linux/libpng \ @@ -155,6 +157,7 @@ OBJECTS = ../actions/CCAction.o \ ../../extensions/GUI/CCControlExtension/CCMenuPassive.o \ ../../extensions/GUI/CCControlExtension/CCScale9Sprite.o \ ../../extensions/GUI/CCControlExtension/CCSpacer.o \ + ../../extensions/network/HttpClient.o \ ../kazmath/src/aabb.o \ ../kazmath/src/plane.o \ ../kazmath/src/vec2.o \ diff --git a/extensions/Android.mk b/extensions/Android.mk index d734526085..77ababacb1 100644 --- a/extensions/Android.mk +++ b/extensions/Android.mk @@ -34,16 +34,19 @@ GUI/CCControlExtension/CCInvocation.cpp \ GUI/CCControlExtension/CCMenuPassive.cpp \ GUI/CCControlExtension/CCScale9Sprite.cpp \ GUI/CCControlExtension/CCSpacer.cpp \ -GUI/CCScrollView/CCScrollView.cpp +GUI/CCScrollView/CCScrollView.cpp \ +network/HttpClient.cpp LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static +LOCAL_WHOLE_STATIC_LIBRARIES += cocos_curl_static LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) \ $(LOCAL_PATH)/CCBReader \ - $(LOCAL_PATH)/UI/CCControlExtension \ - $(LOCAL_PATH)/UI/CCScrollView + $(LOCAL_PATH)/GUI/CCControlExtension \ + $(LOCAL_PATH)/GUI/CCScrollView \ + $(LOCAL_PATH)/network - include $(BUILD_STATIC_LIBRARY) $(call import-module,cocos2dx) +$(call import-module,cocos2dx/platform/third_party/android/prebuilt/libcurl) diff --git a/extensions/Network/HttpClient.cpp b/extensions/Network/HttpClient.cpp index 9e78b55348..84eb2c18b5 100644 --- a/extensions/Network/HttpClient.cpp +++ b/extensions/Network/HttpClient.cpp @@ -79,15 +79,15 @@ size_t writeData(void *ptr, size_t size, size_t nmemb, void *stream) // 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 processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *errorCode); +int processPostTask(CCHttpRequest *request, 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; + CCHttpRequest *request = NULL; while (true) { @@ -109,8 +109,9 @@ static void* networkThread(void *data) 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); + request = dynamic_cast(s_requestQueue->objectAtIndex(0)); + s_requestQueue->removeObjectAtIndex(0); + // request's refcount = 1 here } pthread_mutex_unlock(&s_requestQueueMutex); @@ -122,22 +123,26 @@ static void* networkThread(void *data) // step 2: libcurl sync access // Create a HttpResponse object, the default setting is http access failed - HttpResponse *response = new HttpResponse(request); + CCHttpResponse *response = new CCHttpResponse(request); - int responseCode = CURL_LAST; + // request's refcount = 2 here, it's retained by HttpRespose constructor + request->release(); + // ok, refcount = 1 now, only HttpResponse hold it. + + int responseCode = -1; int retValue = 0; // Process the request -> get response packet switch (request->getRequestType()) { - case HttpRequest::kHttpGet: // HTTP GET + case CCHttpRequest::kHttpGet: // HTTP GET retValue = processGetTask(request, writeData, response->getResponseData(), &responseCode); break; - case HttpRequest::kHttpPost: // HTTP POST + case CCHttpRequest::kHttpPost: // HTTP POST retValue = processPostTask(request, writeData, response->getResponseData(), @@ -161,7 +166,7 @@ static void* networkThread(void *data) { response->setSucceed(true); } - + // add response packet into queue pthread_mutex_lock(&s_responseQueueMutex); @@ -174,18 +179,9 @@ static void* networkThread(void *data) // 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); - + s_asyncRequestCount -= s_requestQueue->count(); if (s_pSem != NULL) { #if CC_ASYNC_HTTPREQUEST_USE_NAMED_SEMAPHORE @@ -234,7 +230,7 @@ bool configureCURL(CURL *handle) } //Process Get Request -int processGetTask(HttpRequest *request, write_callback callback, void *stream, int *responseCode) +int processGetTask(CCHttpRequest *request, write_callback callback, void *stream, int *responseCode) { CURLcode code = CURL_LAST; CURL *curl = curl_easy_init(); @@ -284,7 +280,7 @@ int processGetTask(HttpRequest *request, write_callback callback, void *stream, } //Process POST Request -int processPostTask(HttpRequest *request, write_callback callback, void *stream, int32_t *responseCode) +int processPostTask(CCHttpRequest *request, write_callback callback, void *stream, int32_t *responseCode) { CURLcode code = CURL_LAST; CURL *curl = curl_easy_init(); @@ -347,6 +343,8 @@ CCHttpClient* CCHttpClient::getInstance() void CCHttpClient::destroyInstance() { + CCDirector::sharedDirector()->getScheduler()->unscheduleSelector(schedule_selector(CCHttpClient::dispatchResponseCallbacks), + CCHttpClient::getInstance()); CC_SAFE_RELEASE_NULL(s_pHttpClient); } @@ -412,7 +410,7 @@ bool CCHttpClient::lazyInitThreadSemphore() } //Add a get task to queue -void CCHttpClient::send(HttpRequest* request) +void CCHttpClient::send(CCHttpRequest* request) { if (false == lazyInitThreadSemphore()) { @@ -427,10 +425,6 @@ void CCHttpClient::send(HttpRequest* request) ++s_asyncRequestCount; request->retain(); - if (request->getTarget()) - { - request->getTarget()->retain(); - } pthread_mutex_lock(&s_requestQueueMutex); s_requestQueue->addObject(request); @@ -443,13 +437,14 @@ void CCHttpClient::send(HttpRequest* request) // Poll and notify main thread if responses exists in queue void CCHttpClient::dispatchResponseCallbacks(float delta) { - CCLog("CCHttpClient::dispatchResponseCallbacks is running"); - HttpResponse* response = NULL; + // CCLog("CCHttpClient::dispatchResponseCallbacks is running"); + + CCHttpResponse* response = NULL; pthread_mutex_lock(&s_responseQueueMutex); if (s_responseQueue->count()) { - response = dynamic_cast(s_responseQueue->objectAtIndex(0)); + response = dynamic_cast(s_responseQueue->objectAtIndex(0)); s_responseQueue->removeObjectAtIndex(0); } pthread_mutex_unlock(&s_responseQueueMutex); @@ -458,7 +453,7 @@ void CCHttpClient::dispatchResponseCallbacks(float delta) { --s_asyncRequestCount; - HttpRequest *request = response->getHttpRequest(); + CCHttpRequest *request = response->getHttpRequest(); CCObject *pTarget = request->getTarget(); SEL_CallFuncND pSelector = request->getSelector(); diff --git a/extensions/Network/HttpClient.h b/extensions/Network/HttpClient.h index db7bc4f32b..f8d584bd7e 100644 --- a/extensions/Network/HttpClient.h +++ b/extensions/Network/HttpClient.h @@ -54,10 +54,10 @@ public: /** * Add a get request to task queue - * @param request a HttpRequest object, which includes url, response callback etc. + * @param request a CCHttpRequest object, which includes url, response callback etc. * @return NULL */ - void send(HttpRequest* request); + void send(CCHttpRequest* request); /** diff --git a/extensions/Network/HttpRequest.h b/extensions/Network/HttpRequest.h index 0bc101f3ab..89c1a3f812 100644 --- a/extensions/Network/HttpRequest.h +++ b/extensions/Network/HttpRequest.h @@ -32,10 +32,10 @@ NS_CC_EXT_BEGIN /** @brief defines the object which users must packed for CCHttpClient::send(HttpRequest*) method. - Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpRequestTest.cpp as a sample + Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpClientTest.cpp as a sample @since v2.0.2 */ -class HttpRequest : public cocos2d::CCObject +class CCHttpRequest : public cocos2d::CCObject { public: /** Use this enum type as param in setReqeustType(param) */ @@ -52,7 +52,7 @@ public: new/retain/release still works, which means you need to release it manually Please refer to HttpRequestTest.cpp to find its usage */ - HttpRequest() + CCHttpRequest() { _requestType = kHttpUnkown; _url.clear(); @@ -64,7 +64,7 @@ public: }; /** Destructor */ - virtual ~HttpRequest() + virtual ~CCHttpRequest() { if (_pTarget) { diff --git a/extensions/Network/HttpResponse.h b/extensions/Network/HttpResponse.h index 35f426b481..4a5921221f 100644 --- a/extensions/Network/HttpResponse.h +++ b/extensions/Network/HttpResponse.h @@ -33,16 +33,16 @@ NS_CC_EXT_BEGIN /** @brief defines the object which users will receive at onHttpCompleted(sender, HttpResponse) callback - Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpRequestTest.cpp as a sample + Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpClientTest.cpp as a sample @since v2.0.2 */ -class HttpResponse : public cocos2d::CCObject +class CCHttpResponse : public cocos2d::CCObject { public: /** Constructor, it's used by CCHttpClient internal, users don't need to create HttpResponse manually @param request the corresponding HttpRequest which leads to this response */ - HttpResponse(HttpRequest* request) + CCHttpResponse(CCHttpRequest* request) { _pHttpRequest = request; if (_pHttpRequest) @@ -58,7 +58,7 @@ public: /** Destructor, it will be called in CCHttpClient internal, users don't need to desturct HttpResponse object manully */ - virtual ~HttpResponse() + virtual ~CCHttpResponse() { if (_pHttpRequest) { @@ -79,7 +79,7 @@ public: /** Get the corresponding HttpRequest object which leads to this response There's no paired setter for it, coz it's already setted in class constructor */ - inline HttpRequest* getHttpRequest() + inline CCHttpRequest* getHttpRequest() { return _pHttpRequest; } @@ -151,16 +151,15 @@ public: _errorBuffer.clear(); _errorBuffer.assign(value); }; - protected: - bool initWithRequest(HttpRequest* request); + bool initWithRequest(CCHttpRequest* request); // properties - HttpRequest* _pHttpRequest; /// the corresponding HttpRequest pointer who leads to this response + CCHttpRequest* _pHttpRequest; /// the corresponding HttpRequest pointer who leads to this response bool _succeed; /// to indecate if the http reqeust is successful simply std::vector _responseData; /// the returned raw data. You can also dump it as a string - int _responseCode; /// the CURLcode returned from libcurl + int _responseCode; /// the status code returned from libcurl, e.g. 200, 404 std::string _errorBuffer; /// if _responseCode != 200, please read _errorBuffer to find the reason }; diff --git a/samples/TestCpp/Android.mk b/samples/TestCpp/Android.mk index 1bb43009c4..589f8cbe10 100644 --- a/samples/TestCpp/Android.mk +++ b/samples/TestCpp/Android.mk @@ -49,6 +49,7 @@ Classes/ExtensionsTest/CocosBuilderTest/HelloCocosBuilder/HelloCocosBuilderLayer Classes/ExtensionsTest/CocosBuilderTest/TestHeader/TestHeaderLayer.cpp \ Classes/ExtensionsTest/CocosBuilderTest/ButtonTest/ButtonTestLayer.cpp \ Classes/ExtensionsTest/CocosBuilderTest/MenuTest/MenuTestLayer.cpp \ +Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp \ Classes/FontTest/FontTest.cpp \ Classes/IntervalTest/IntervalTest.cpp \ Classes/KeypadTest/KeypadTest.cpp \ @@ -86,8 +87,6 @@ Classes/controller.cpp \ Classes/testBasic.cpp \ Classes/AppDelegate.cpp -LOCAL_STATIC_LIBRARIES := curl_static_prebuilt - LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static @@ -102,5 +101,4 @@ $(call import-module,CocosDenshion/android) $(call import-module,external/Box2D) $(call import-module,external/chipmunk) $(call import-module,cocos2dx) -$(call import-module,cocos2dx/platform/third_party/android/prebuilt/libcurl) $(call import-module,extensions) diff --git a/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/ExtensionsTest.cpp index f55bbd5b4e..d52406744b 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 "NetworkTest/HttpRequestTest.h" +#include "NetworkTest/HttpClientTest.h" enum { @@ -16,7 +16,7 @@ enum TEST_NOTIFICATIONCENTER = 0, TEST_CCCONTROLBUTTON, TEST_COCOSBUILDER, - TEST_HTTPREQUEST, + TEST_HTTPCLIENT, TEST_MAX_COUNT, }; @@ -25,7 +25,7 @@ static const std::string testsName[TEST_MAX_COUNT] = "NotificationCenterTest", "CCControlButtonTest", "CocosBuilderTest", - "HttpRequestTest", + "HttpClientTest", }; //////////////////////////////////////////////////////// @@ -83,9 +83,9 @@ void ExtensionsMainLayer::menuCallback(CCObject* pSender) } } break; - case TEST_HTTPREQUEST: + case TEST_HTTPCLIENT: { - runHttpRequestTest(); + runHttpClientTest(); } default: break; diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp new file mode 100644 index 0000000000..d963cc2c17 --- /dev/null +++ b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp @@ -0,0 +1,194 @@ +#include "HttpClientTest.h" +#include "../ExtensionsTest.h" +#include + +USING_NS_CC; +USING_NS_CC_EXT; + +HttpClientTest::HttpClientTest() +: m_labelStatusCode(NULL) +{ + CCSize winSize = CCDirector::sharedDirector()->getWinSize(); + + CCLabelTTF *label = CCLabelTTF::create("Http Request Test", "Arial", 28); + label->setPosition(ccp(winSize.width / 2, winSize.height - 50)); + addChild(label, 0); + + CCMenu *menuRequest = CCMenu::create(); + menuRequest->setPosition(CCPointZero); + addChild(menuRequest); + + // Get + CCLabelTTF *labelGet = CCLabelTTF::create("Test Get", "Arial", 22); + CCMenuItemLabel *itemGet = CCMenuItemLabel::create(labelGet, this, menu_selector(HttpClientTest::onMenuGetTestClicked)); + itemGet->setPosition(ccp(winSize.width / 2, winSize.height - 100)); + menuRequest->addChild(itemGet); + + // Post + CCLabelTTF *labelPost = CCLabelTTF::create("Test Post", "Arial", 22); + CCMenuItemLabel *itemPost = CCMenuItemLabel::create(labelPost, this, menu_selector(HttpClientTest::onMenuPostTestClicked)); + itemPost->setPosition(ccp(winSize.width / 2, winSize.height - 150)); + menuRequest->addChild(itemPost); + + // Post Binary + CCLabelTTF *labelPostBinary = CCLabelTTF::create("Test Post Binary", "Arial", 22); + CCMenuItemLabel *itemPostBinary = CCMenuItemLabel::create(labelPostBinary, this, menu_selector(HttpClientTest::onMenuPostBinaryTestClicked)); + itemPostBinary->setPosition(ccp(winSize.width / 2, winSize.height - 200)); + menuRequest->addChild(itemPostBinary); + + // Response Code Label + m_labelStatusCode = CCLabelTTF::create("HTTP Status Code", "Marker Felt", 20); + m_labelStatusCode->setPosition(ccp(winSize.width / 2, winSize.height - 250)); + addChild(m_labelStatusCode); + + // Back Menu + CCMenuItemFont *itemBack = CCMenuItemFont::create("Back", this, menu_selector(HttpClientTest::toExtensionsMainLayer)); + itemBack->setPosition(ccp(winSize.width - 50, 25)); + CCMenu *menuBack = CCMenu::create(itemBack, NULL); + menuBack->setPosition(CCPointZero); + addChild(menuBack); +} + +HttpClientTest::~HttpClientTest() +{ + CCHttpClient::getInstance()->destroyInstance(); +} + +void HttpClientTest::onMenuGetTestClicked(cocos2d::CCObject *sender) +{ + // test 1 + { + CCHttpRequest* request = new CCHttpRequest(); + request->setUrl("http://just-make-this-request-failed.com"); + request->setRequestType(CCHttpRequest::kHttpGet); + request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted)); + request->setTag("GET test1"); + CCHttpClient::getInstance()->send(request); + request->release(); + } + + // test 2 + { + CCHttpRequest* request = new CCHttpRequest(); + // required fields + request->setUrl("http://www.httpbin.org/ip"); + request->setRequestType(CCHttpRequest::kHttpGet); + request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted)); + // optional fields + request->setTag("GET test2"); + + CCHttpClient::getInstance()->send(request); + + // don't forget to release it, pair to new + request->release(); + } + + // test 3 + { + CCHttpRequest* request = new CCHttpRequest(); + request->setUrl("http://www.httpbin.org/get"); + request->setRequestType(CCHttpRequest::kHttpGet); + request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted)); + request->setTag("GET test3"); + CCHttpClient::getInstance()->send(request); + request->release(); + } + + // waiting + m_labelStatusCode->setString("waiting..."); + +} + +void HttpClientTest::onMenuPostTestClicked(cocos2d::CCObject *sender) +{ + CCHttpRequest* request = new CCHttpRequest(); + request->setUrl("http://www.httpbin.org/post"); + request->setRequestType(CCHttpRequest::kHttpPost); + request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted)); + + // write the post data + const char* postData = "visitor=cocos2d&TestSuite=Extensions Test/NetowrkTest"; + request->setRequestData(postData, strlen(postData)); + + request->setTag("POST test"); + CCHttpClient::getInstance()->send(request); + request->release(); + + // waiting + m_labelStatusCode->setString("waiting..."); +} + +void HttpClientTest::onMenuPostBinaryTestClicked(cocos2d::CCObject *sender) +{ + CCHttpRequest* request = new CCHttpRequest(); + request->setUrl("http://www.httpbin.org/post"); + request->setRequestType(CCHttpRequest::kHttpPost); + request->setResponseCallback(this, callfuncND_selector(HttpClientTest::onHttpRequestCompleted)); + + // write the post data + char postData[22] = "binary=hello\0\0cocos2d"; // including \0, the strings after \0 should not be cut in response + request->setRequestData(postData, 22); + + request->setTag("POST Binary test"); + CCHttpClient::getInstance()->send(request); + request->release(); + + // waiting + m_labelStatusCode->setString("waiting..."); +} + +void HttpClientTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *data) +{ + CCHttpResponse *response = (CCHttpResponse*)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()); + } + + int statusCode = response->getResponseCode(); + char statusString[64] = {}; + sprintf(statusString, "HTTP Status Code: %d, tag = %s", statusCode, response->getHttpRequest()->getTag()); + m_labelStatusCode->setString(statusString); + CCLog("response code: %d", statusCode); + + if (!response->isSucceed()) + { + CCLog("response failed"); + CCLog("error buffer: %s", response->getErrorBuffer()); + return; + } + + // dump data + std::vector *buffer = response->getResponseData(); + printf("Http Test, dump data: "); + for (int i = 0; i < buffer->size(); i++) + { + printf("%c", (*buffer)[i]); + } + printf("\n"); +} + +void HttpClientTest::toExtensionsMainLayer(cocos2d::CCObject *sender) +{ + ExtensionsTestScene *pScene = new ExtensionsTestScene(); + pScene->runThisTest(); + pScene->release(); +} + +void runHttpClientTest() +{ + CCScene *pScene = CCScene::create(); + HttpClientTest *pLayer = new HttpClientTest(); + pScene->addChild(pLayer); + + CCDirector::sharedDirector()->replaceScene(pScene); + pLayer->release(); +} + diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h new file mode 100644 index 0000000000..544bf6d94e --- /dev/null +++ b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h @@ -0,0 +1,28 @@ +#ifndef __HTTP_CLIENT_H__ +#define __HTTP_CLIENT_H__ + +#include "cocos2d.h" +#include "cocos-ext.h" + +class HttpClientTest : public cocos2d::CCLayer +{ +public: + HttpClientTest(); + virtual ~HttpClientTest(); + void toExtensionsMainLayer(cocos2d::CCObject *sender); + + //Menu Callbacks + void onMenuGetTestClicked(cocos2d::CCObject *sender); + void onMenuPostTestClicked(cocos2d::CCObject *sender); + void onMenuPostBinaryTestClicked(cocos2d::CCObject *sender); + + //Http Response Callback + void onHttpRequestCompleted(cocos2d::CCObject *sender, void *data); + +private: + cocos2d::CCLabelTTF* m_labelStatusCode; +}; + +void runHttpClientTest(); + +#endif //__HTTPREQUESTHTTP_H diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp deleted file mode 100644 index 80cb18326e..0000000000 --- a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "HttpRequestTest.h" -#include "../ExtensionsTest.h" - -#define GETURL "http://www.google.com" -#define POSTURL "http://www.replacewithyours.com/post.php" -#define DOWNLOADURL "http://www.google.com/index.html" - -USING_NS_CC; -USING_NS_CC_EXT; - -HttpRequestTest::HttpRequestTest() : m_labelStatusCode(NULL) -{ - CCSize winSize = CCDirector::sharedDirector()->getWinSize(); - - CCLabelTTF *label = CCLabelTTF::create("Http Request Test", "Arial", 28); - label->setPosition(ccp(winSize.width / 2, winSize.height - 50)); - addChild(label, 0); - - CCMenu *menuRequest = CCMenu::create(); - menuRequest->setPosition(CCPointZero); - addChild(menuRequest); - - //Get - CCLabelTTF *labelGet = CCLabelTTF::create("Test Get", "Arial", 15); - CCMenuItemLabel *itemGet = CCMenuItemLabel::create(labelGet, this, menu_selector(HttpRequestTest::onLabelGetTestClicked)); - itemGet->setPosition(ccp(winSize.width / 2, winSize.height - 100)); - menuRequest->addChild(itemGet); - - //Post - CCLabelTTF *labelPost = CCLabelTTF::create("Test Post", "Arial", 15); - CCMenuItemLabel *itemPost = CCMenuItemLabel::create(labelPost, this, menu_selector(HttpRequestTest::onLabelPostTestClicked)); - itemPost->setPosition(ccp(winSize.width / 2, winSize.height - 130)); - menuRequest->addChild(itemPost); - - //Post Binary - CCLabelTTF *labelPostBinary = CCLabelTTF::create("Test Post Binary", "Arial", 15); - CCMenuItemLabel *itemPostBinary = CCMenuItemLabel::create(labelPostBinary, this, menu_selector(HttpRequestTest::onLabelPostBinaryTestClicked)); - itemPostBinary->setPosition(ccp(winSize.width / 2, winSize.height - 160)); - menuRequest->addChild(itemPostBinary); - - //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)); - addChild(labelStatus); - setLabelStatusCode(labelStatus); - - //Back Menu - CCMenuItemFont *itemBack = CCMenuItemFont::create("Back", this, menu_selector(HttpRequestTest::toExtensionsMainLayer)); - itemBack->setPosition(ccp(winSize.width - 50, 25)); - CCMenu *menuBack = CCMenu::create(itemBack, NULL); - menuBack->setPosition(CCPointZero); - addChild(menuBack); -} - -void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender) -{ - /***** - test 1 - ******/ - - HttpRequest* request1 = new HttpRequest(); - // required fields - request1->setUrl("http://www.httpbin.org/ip"); - 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.httpbin.org/get"); - 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) -{ - HttpRequest* request = new HttpRequest(); - request->setUrl("http://www.httpbin.org/post"); - request->setRequestType(HttpRequest::kHttpPost); - request->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); - - // write the post data - const char* postData = "visitor=cocos2d&TestSuite=Extensions Test/NetowrkTest"; - request->setRequestData(postData, strlen(postData)); - - request->setTag("HttpRequestTest: POST JSON"); - CCHttpClient::getInstance()->send(request); - request->release(); -} - -void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender) -{ - HttpRequest* request = new HttpRequest(); - request->setUrl("http://www.httpbin.org/post"); - request->setRequestType(HttpRequest::kHttpPost); - request->setResponseCallback(this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted)); - - // write the post data - char postData[22] = "binary=hello\0\0cocos2d"; // including \0, the strings after \0 should not be cut in response - request->setRequestData(postData, 22); - - request->setTag("HttpRequestTest: POST Binary"); - CCHttpClient::getInstance()->send(request); - request->release(); -} - -void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *data) -{ - 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; - } - - // dump data - std::vector *buffer = response->getResponseData(); - printf("Http Test, dump data: "); - for (int i = 0; i < buffer->size(); i++) - { - printf("%c", (*buffer)[i]); - } - printf("\n"); -} - -void HttpRequestTest::toExtensionsMainLayer(cocos2d::CCObject *sender) -{ - ExtensionsTestScene *pScene = new ExtensionsTestScene(); - pScene->runThisTest(); - pScene->release(); -} - -void runHttpRequestTest() -{ - CCScene *pScene = CCScene::create(); - HttpRequestTest *pLayer = new HttpRequestTest(); - pScene->addChild(pLayer); - - CCDirector::sharedDirector()->replaceScene(pScene); - pLayer->release(); -} - diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.h b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.h deleted file mode 100644 index bb1a387354..0000000000 --- a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpRequestTest.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __HTTPREQUESTHTTP_H -#define __HTTPREQUESTHTTP_H - -#include "cocos2d.h" -#include "cocos-ext.h" - -class HttpRequestTest : public cocos2d::CCLayer -{ - CC_SYNTHESIZE(CCLabelTTF *, m_labelStatusCode, LabelStatusCode); -public: - HttpRequestTest(); - void toExtensionsMainLayer(cocos2d::CCObject *sender); - - //Menu Callbacks - void onLabelGetTestClicked(cocos2d::CCObject *sender); - void onLabelPostTestClicked(cocos2d::CCObject *sender); - void onLabelPostBinaryTestClicked(cocos2d::CCObject *sender); - void onLabelDownloadTestClicked(cocos2d::CCObject *sender); - - //Http Response Callback - void onHttpRequestCompleted(cocos2d::CCObject *sender, void *data); -}; - -void runHttpRequestTest(); - -#endif //__HTTPREQUESTHTTP_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 72d497708a..fd8eccb0f6 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 @@ -afa666f94104aca6725fb837f7c037ba586c0fbd \ No newline at end of file +86f6b6c3837508de917c08ed3e087b901da16e27 \ No newline at end of file diff --git a/samples/TestCpp/proj.linux/Makefile b/samples/TestCpp/proj.linux/Makefile index c0eb76b33a..8948ccd3c4 100644 --- a/samples/TestCpp/proj.linux/Makefile +++ b/samples/TestCpp/proj.linux/Makefile @@ -21,6 +21,7 @@ INCLUDES = -I../ \ -I$(COCOS2DX_PATH)/platform/third_party/linux/glew-1.7.0/glew-1.7.0/include/ \ -I$(COCOS2DX_PATH)/platform/third_party/linux/libxml2 \ -I$(COCOS2DX_PATH)/platform/third_party/linux/libjpeg \ + -I$(COCOS2DX_PATH)/platform/third_party/linux/curl \ -I../../../CocosDenshion/include \ -I../../../extensions/ \ -I../../../external/ \ @@ -78,6 +79,7 @@ OBJECTS = ../Classes/AccelerometerTest/AccelerometerTest.o \ ../Classes/ExtensionsTest/ControlExtensionTest/CCControlSwitchTest/CCControlSwitchTest.o \ ../Classes/ExtensionsTest/ExtensionsTest.o \ ../Classes/ExtensionsTest/NotificationCenterTest/NotificationCenterTest.o \ + ../Classes/ExtensionsTest/NetworkTest/HttpClientTest.o \ ../Classes/FontTest/FontTest.o \ ../Classes/IntervalTest/IntervalTest.o \ ../Classes/KeypadTest/KeypadTest.o \ diff --git a/samples/TestCpp/proj.mac/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id b/samples/TestCpp/proj.mac/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id index 441588dd65..63719a1921 100644 --- a/samples/TestCpp/proj.mac/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id +++ b/samples/TestCpp/proj.mac/TestCpp.xcodeproj/project.pbxproj.REMOVED.git-id @@ -1 +1 @@ -04e861a3cc213902d2d09aad426257c2e326b22c \ No newline at end of file +30057061474e27b1b05c7ad738c37006a78bcba0 \ No newline at end of file From 7863375cf8a278c18acce9c1cb72581eb6e9f3db Mon Sep 17 00:00:00 2001 From: Walzer Date: Thu, 9 Aug 2012 11:15:06 +0800 Subject: [PATCH 08/11] issue #1424, http wrapper compiled pass on linux --- cocos2dx/proj.linux/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cocos2dx/proj.linux/Makefile b/cocos2dx/proj.linux/Makefile index a8c503bc4a..cb43147482 100644 --- a/cocos2dx/proj.linux/Makefile +++ b/cocos2dx/proj.linux/Makefile @@ -24,7 +24,7 @@ INCLUDES = -I.. \ -I../platform/third_party/linux/libpng \ -I../platform/third_party/linux/libjpeg \ -I../platform/third_party/linux/libtiff/include \ - -I../platform/third_party/linux/curl/include \ + -I../platform/third_party/linux/ \ # -I../platform/third_party/linux/libpng \ From d2cfdf1d2fce04e198969ec4d2a351f82f2f1e9c Mon Sep 17 00:00:00 2001 From: Walzer Date: Thu, 9 Aug 2012 11:43:08 +0800 Subject: [PATCH 09/11] change Network to Network-rename --- extensions/{Network => network-rename}/HttpClient.cpp | 0 extensions/{Network => network-rename}/HttpClient.h | 0 extensions/{Network => network-rename}/HttpRequest.h | 0 extensions/{Network => network-rename}/HttpResponse.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename extensions/{Network => network-rename}/HttpClient.cpp (100%) rename extensions/{Network => network-rename}/HttpClient.h (100%) rename extensions/{Network => network-rename}/HttpRequest.h (100%) rename extensions/{Network => network-rename}/HttpResponse.h (100%) diff --git a/extensions/Network/HttpClient.cpp b/extensions/network-rename/HttpClient.cpp similarity index 100% rename from extensions/Network/HttpClient.cpp rename to extensions/network-rename/HttpClient.cpp diff --git a/extensions/Network/HttpClient.h b/extensions/network-rename/HttpClient.h similarity index 100% rename from extensions/Network/HttpClient.h rename to extensions/network-rename/HttpClient.h diff --git a/extensions/Network/HttpRequest.h b/extensions/network-rename/HttpRequest.h similarity index 100% rename from extensions/Network/HttpRequest.h rename to extensions/network-rename/HttpRequest.h diff --git a/extensions/Network/HttpResponse.h b/extensions/network-rename/HttpResponse.h similarity index 100% rename from extensions/Network/HttpResponse.h rename to extensions/network-rename/HttpResponse.h From 9d6e37cf73d506d47083df6b448ff66da28eef22 Mon Sep 17 00:00:00 2001 From: Walzer Date: Thu, 9 Aug 2012 11:44:24 +0800 Subject: [PATCH 10/11] issue #1424, rename "Network" to "network" --- extensions/{network-rename => network}/HttpClient.cpp | 0 extensions/{network-rename => network}/HttpClient.h | 0 extensions/{network-rename => network}/HttpRequest.h | 0 extensions/{network-rename => network}/HttpResponse.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename extensions/{network-rename => network}/HttpClient.cpp (100%) rename extensions/{network-rename => network}/HttpClient.h (100%) rename extensions/{network-rename => network}/HttpRequest.h (100%) rename extensions/{network-rename => network}/HttpResponse.h (100%) diff --git a/extensions/network-rename/HttpClient.cpp b/extensions/network/HttpClient.cpp similarity index 100% rename from extensions/network-rename/HttpClient.cpp rename to extensions/network/HttpClient.cpp diff --git a/extensions/network-rename/HttpClient.h b/extensions/network/HttpClient.h similarity index 100% rename from extensions/network-rename/HttpClient.h rename to extensions/network/HttpClient.h diff --git a/extensions/network-rename/HttpRequest.h b/extensions/network/HttpRequest.h similarity index 100% rename from extensions/network-rename/HttpRequest.h rename to extensions/network/HttpRequest.h diff --git a/extensions/network-rename/HttpResponse.h b/extensions/network/HttpResponse.h similarity index 100% rename from extensions/network-rename/HttpResponse.h rename to extensions/network/HttpResponse.h From 3ff21fe275bfb5025e9b0dd209696f47a54e00bf Mon Sep 17 00:00:00 2001 From: James Chen Date: Thu, 9 Aug 2012 17:04:55 +0800 Subject: [PATCH 11/11] fixed #1424: http wrapper compiled ok on win32. --- extensions/network/HttpClient.cpp | 4 + extensions/network/HttpClient.h | 2 +- extensions/proj.win32/libExtensions.vcproj | 24 +++++- extensions/proj.win32/libExtensions.vcxproj | 10 ++- .../proj.win32/libExtensions.vcxproj.filters | 77 +++++++++++-------- .../NetworkTest/HttpClientTest.cpp | 2 +- .../NetworkTest/HttpClientTest.h | 2 +- samples/TestCpp/proj.win32/TestCpp.vcproj | 20 ++++- samples/TestCpp/proj.win32/TestCpp.vcxproj | 6 +- .../proj.win32/TestCpp.vcxproj.filters | 9 +++ 10 files changed, 111 insertions(+), 45 deletions(-) diff --git a/extensions/network/HttpClient.cpp b/extensions/network/HttpClient.cpp index 84eb2c18b5..9227771575 100644 --- a/extensions/network/HttpClient.cpp +++ b/extensions/network/HttpClient.cpp @@ -53,6 +53,10 @@ static unsigned long s_asyncRequestCount = 0; static sem_t s_sem; #endif +#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) +typedef int int32_t; +#endif + static bool need_quit = false; static CCArray* s_requestQueue = NULL; diff --git a/extensions/network/HttpClient.h b/extensions/network/HttpClient.h index f8d584bd7e..67b91ebaf0 100644 --- a/extensions/network/HttpClient.h +++ b/extensions/network/HttpClient.h @@ -43,7 +43,7 @@ NS_CC_EXT_BEGIN /** @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 +class CCHttpClient : public cocos2d::CCObject { public: /** Return the shared instance **/ diff --git a/extensions/proj.win32/libExtensions.vcproj b/extensions/proj.win32/libExtensions.vcproj index 9f38fd6c49..81df3bbfce 100644 --- a/extensions/proj.win32/libExtensions.vcproj +++ b/extensions/proj.win32/libExtensions.vcproj @@ -41,7 +41,7 @@ + + + + + + + + + + diff --git a/extensions/proj.win32/libExtensions.vcxproj b/extensions/proj.win32/libExtensions.vcxproj index 9d757e0d63..179c857b6a 100644 --- a/extensions/proj.win32/libExtensions.vcxproj +++ b/extensions/proj.win32/libExtensions.vcxproj @@ -1,4 +1,4 @@ - + @@ -51,7 +51,7 @@ Disabled - $(SolutionDir)cocos2dx;$(SolutionDir)cocos2dx\include;$(SolutionDir)cocos2dx\kazmath\include;$(SolutionDir)cocos2dx\platform\win32;$(SolutionDir)cocos2dx\platform\third_party\win32\OGLES;..\;%(AdditionalIncludeDirectories) + $(SolutionDir)cocos2dx;$(SolutionDir)cocos2dx\include;$(SolutionDir)cocos2dx\kazmath\include;$(SolutionDir)cocos2dx\platform\win32;$(SolutionDir)cocos2dx\platform\third_party\win32;$(SolutionDir)cocos2dx\platform\third_party\win32\pthread;$(SolutionDir)cocos2dx\platform\third_party\win32\OGLES;..\;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;_DEBUG;_LIB;COCOS2D_DEBUG=1;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks @@ -67,7 +67,7 @@ MaxSpeed true - $(SolutionDir)cocos2dx;$(SolutionDir)cocos2dx\include;$(SolutionDir)cocos2dx\kazmath\include;$(SolutionDir)cocos2dx\platform\win32;$(SolutionDir)cocos2dx\platform\third_party\win32\OGLES;..\;%(AdditionalIncludeDirectories) + $(SolutionDir)cocos2dx;$(SolutionDir)cocos2dx\include;$(SolutionDir)cocos2dx\kazmath\include;$(SolutionDir)cocos2dx\platform\win32;$(SolutionDir)cocos2dx\platform\third_party\win32;$(SolutionDir)cocos2dx\platform\third_party\win32\pthread;$(SolutionDir)cocos2dx\platform\third_party\win32\OGLES;..\;%(AdditionalIncludeDirectories) WIN32;_WINDOWS;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) MultiThreadedDLL true @@ -109,6 +109,7 @@ + @@ -148,6 +149,9 @@ + + + diff --git a/extensions/proj.win32/libExtensions.vcxproj.filters b/extensions/proj.win32/libExtensions.vcxproj.filters index 19e11451ea..159ba81c79 100644 --- a/extensions/proj.win32/libExtensions.vcxproj.filters +++ b/extensions/proj.win32/libExtensions.vcxproj.filters @@ -1,18 +1,21 @@ - + {d37545ef-285b-4315-9fca-40da6fc2a6c9} - + {202b519b-b5e0-499f-b3b8-ed5da144b248} - + {c07abd14-e9dd-4e2d-85c4-a180070161b4} - + {46797895-f71d-4ddb-b381-d0884e678d39} + + {2a7741ff-87a5-41c8-8e51-d7a1cf0c8e4d} + @@ -67,43 +70,46 @@ CCBReader - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCScrollView + GUI\CCScrollView + + + GUI\network @@ -171,48 +177,57 @@ CCBReader - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCControlExtension + GUI\CCControlExtension - UI\CCScrollView + GUI\CCScrollView + + GUI\network + + + GUI\network + + + GUI\network + \ No newline at end of file diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp index d963cc2c17..1b07cc3b49 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp +++ b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.cpp @@ -137,7 +137,7 @@ void HttpClientTest::onMenuPostBinaryTestClicked(cocos2d::CCObject *sender) m_labelStatusCode->setString("waiting..."); } -void HttpClientTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *data) +void HttpClientTest::onHttpRequestCompleted(cocos2d::CCNode *sender, void *data) { CCHttpResponse *response = (CCHttpResponse*)data; diff --git a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h index 544bf6d94e..41516661d9 100644 --- a/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h +++ b/samples/TestCpp/Classes/ExtensionsTest/NetworkTest/HttpClientTest.h @@ -17,7 +17,7 @@ public: void onMenuPostBinaryTestClicked(cocos2d::CCObject *sender); //Http Response Callback - void onHttpRequestCompleted(cocos2d::CCObject *sender, void *data); + void onHttpRequestCompleted(cocos2d::CCNode *sender, void *data); private: cocos2d::CCLabelTTF* m_labelStatusCode; diff --git a/samples/TestCpp/proj.win32/TestCpp.vcproj b/samples/TestCpp/proj.win32/TestCpp.vcproj index 73b7f9cc22..0ac12a7742 100644 --- a/samples/TestCpp/proj.win32/TestCpp.vcproj +++ b/samples/TestCpp/proj.win32/TestCpp.vcproj @@ -41,7 +41,7 @@ + + + + + + 4267;4251;4244;%(DisableSpecificWarnings) - libExtensions.lib;libcocos2d.lib;libCocosDenshion.lib;opengl32.lib;glew32.lib;libBox2d.lib;libchipmunk.lib;libcurl_imp.lib;%(AdditionalDependencies) + libExtensions.lib;libcocos2d.lib;libCocosDenshion.lib;opengl32.lib;glew32.lib;libBox2d.lib;libchipmunk.lib;libcurl_imp.lib;pthreadVCE2.lib;%(AdditionalDependencies) $(OutDir)$(ProjectName).exe $(OutDir);%(AdditionalLibraryDirectories) true @@ -92,7 +92,7 @@ 4267;4251;4244;%(DisableSpecificWarnings) - libcocos2d.lib;libCocosDenshion.lib;opengl32.lib;glew32.lib;libBox2d.lib;libchipmunk.lib;libcurl_imp.lib;libExtensions.lib;%(AdditionalDependencies) + libcocos2d.lib;libCocosDenshion.lib;opengl32.lib;glew32.lib;libBox2d.lib;libchipmunk.lib;libcurl_imp.lib;libExtensions.lib;pthreadVCE2.lib;%(AdditionalDependencies) $(OutDir)$(ProjectName).exe $(OutDir);%(AdditionalLibraryDirectories) true @@ -107,6 +107,7 @@ + @@ -188,6 +189,7 @@ + diff --git a/samples/TestCpp/proj.win32/TestCpp.vcxproj.filters b/samples/TestCpp/proj.win32/TestCpp.vcxproj.filters index 04daaf6d93..59648259a0 100644 --- a/samples/TestCpp/proj.win32/TestCpp.vcxproj.filters +++ b/samples/TestCpp/proj.win32/TestCpp.vcxproj.filters @@ -187,6 +187,9 @@ {b3be049f-74ff-44a5-8895-0766e4807671} + + {28e8af6a-0e73-4069-bdf4-735138ed886d} + @@ -426,6 +429,9 @@ Classes\MutiTouchTest + + Classes\ExtensionsTest\NetworkTest + @@ -839,5 +845,8 @@ Classes\MutiTouchTest + + Classes\ExtensionsTest\NetworkTest + \ No newline at end of file