mirror of https://github.com/axmolengine/axmol.git
issue #1424, http-get works ok. Tomorrow I will focus on post.
This commit is contained in:
parent
39e45da8d9
commit
b094f70ab3
|
@ -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 <queue>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <semaphore.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#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<HttpRequestPacket *> *s_requestQueue = NULL;
|
|
||||||
static std::queue<HttpResponsePacket *> *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<HttpRequestPacket *> *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<std::string>::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<HttpRequestPacket *>();
|
|
||||||
s_responseQueue = new std::queue<HttpResponsePacket *>();
|
|
||||||
|
|
||||||
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<char> &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<std::string> &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<HttpResponsePacket *> *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
|
|
||||||
|
|
||||||
|
|
|
@ -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<std::string> 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<char> &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<std::string> &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__
|
|
|
@ -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 <queue>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#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<char> *recvBuffer = (std::vector<char>*)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<HttpRequest*>(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<HttpRequest*>(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<HttpResponse*>(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
|
||||||
|
|
||||||
|
|
|
@ -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<std::string> &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__
|
|
@ -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<char> 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<char> _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__
|
|
@ -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<char>* data)
|
||||||
|
{
|
||||||
|
_responseData = *data;
|
||||||
|
}
|
||||||
|
inline std::vector<char>* 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<char> _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__
|
|
@ -28,7 +28,9 @@
|
||||||
#include "GUI/CCControlExtension/CCControlExtensions.h"
|
#include "GUI/CCControlExtension/CCControlExtensions.h"
|
||||||
#include "GUI/CCScrollView/CCScrollView.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__ */
|
#endif /* __COCOS2D_EXT_H__ */
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "NotificationCenterTest/NotificationCenterTest.h"
|
#include "NotificationCenterTest/NotificationCenterTest.h"
|
||||||
#include "ControlExtensionTest/CCControlSceneManager.h"
|
#include "ControlExtensionTest/CCControlSceneManager.h"
|
||||||
#include "CocosBuilderTest/CocosBuilderTest.h"
|
#include "CocosBuilderTest/CocosBuilderTest.h"
|
||||||
#include "HttpRequestTest/HttpRequestTest.h"
|
#include "NetworkTest/HttpRequestTest.h"
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,12 +38,6 @@ HttpRequestTest::HttpRequestTest() : m_labelStatusCode(NULL)
|
||||||
itemPostBinary->setPosition(ccp(winSize.width / 2, winSize.height - 160));
|
itemPostBinary->setPosition(ccp(winSize.width / 2, winSize.height - 160));
|
||||||
menuRequest->addChild(itemPostBinary);
|
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
|
//Response Code Label
|
||||||
CCLabelTTF *labelStatus = CCLabelTTF::create("Notice: Replace Post Url With Your Own", "Marker Felt", 20);
|
CCLabelTTF *labelStatus = CCLabelTTF::create("Notice: Replace Post Url With Your Own", "Marker Felt", 20);
|
||||||
labelStatus->setPosition(ccp(winSize.width / 2, winSize.height - 250));
|
labelStatus->setPosition(ccp(winSize.width / 2, winSize.height - 250));
|
||||||
|
@ -60,25 +54,64 @@ HttpRequestTest::HttpRequestTest() : m_labelStatusCode(NULL)
|
||||||
|
|
||||||
void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender)
|
void HttpRequestTest::onLabelGetTestClicked(cocos2d::CCObject *sender)
|
||||||
{
|
{
|
||||||
string url = GETURL;
|
/*****
|
||||||
|
test 1
|
||||||
|
******/
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest();
|
|
||||||
requestor->addGetTask(url, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequestTest::onLabelPostTestClicked(cocos2d::CCObject *sender)
|
void HttpRequestTest::onLabelPostTestClicked(cocos2d::CCObject *sender)
|
||||||
{
|
{
|
||||||
string url = POSTURL;
|
const char* content = "username=a\0b";
|
||||||
string content = "username=hello&password=world";
|
|
||||||
|
|
||||||
CCHttpRequest *requestor = CCHttpRequest::sharedHttpRequest();
|
HttpRequest* request = new HttpRequest();
|
||||||
//You may name the request
|
request->autorelease();
|
||||||
requestor->setReqId("login");
|
|
||||||
requestor->addPostTask(url, content, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted));
|
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)
|
void HttpRequestTest::onLabelPostBinaryTestClicked(cocos2d::CCObject *sender)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
string url = POSTURL;
|
string url = POSTURL;
|
||||||
const char *content = "username=a\0b";
|
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);
|
vec.insert(vec.end(), content, content + strlen(content) + 2);
|
||||||
requestor->setReqId("postbinary");
|
requestor->setReqId("postbinary");
|
||||||
requestor->addPostTask(url, vec, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted));
|
requestor->addPostTask(url, vec, this, callfuncND_selector(HttpRequestTest::onHttpRequestCompleted));
|
||||||
}
|
*/
|
||||||
|
|
||||||
void HttpRequestTest::onLabelDownloadTestClicked(cocos2d::CCObject *sender)
|
|
||||||
{
|
|
||||||
string url = DOWNLOADURL;
|
|
||||||
vector<string> 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)
|
void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *data)
|
||||||
{
|
{
|
||||||
HttpResponsePacket *response = (HttpResponsePacket *)data;
|
HttpResponse *response = (HttpResponse*)data;
|
||||||
|
|
||||||
//You can get original request type from: response->request->reqType
|
if (!response)
|
||||||
|
{
|
||||||
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;
|
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<char> *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()
|
//If the response is binary, use response->responseData.data() and response->responseData.length()
|
||||||
//To process the response
|
//To process the response
|
||||||
|
|
||||||
|
/*
|
||||||
if (response->responseData.length() >= kMaxLogLen) {
|
if (response->responseData.length() >= kMaxLogLen) {
|
||||||
response->responseData = response->responseData.substr(0, kMaxLogLen / 2);
|
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") {
|
if (response->request->reqId == "postbinary") {
|
||||||
int32_t length = response->responseData.length();
|
int32_t length = response->responseData.length();
|
||||||
const char *data = response->responseData.data();
|
const char *data = response->responseData.data();
|
||||||
|
@ -138,13 +182,8 @@ void HttpRequestTest::onHttpRequestCompleted(cocos2d::CCObject *sender, void *da
|
||||||
CCLog("%c", data[i]);
|
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)
|
void HttpRequestTest::toExtensionsMainLayer(cocos2d::CCObject *sender)
|
|
@ -1 +1 @@
|
||||||
eae8f176936098bc77ca4472b94f9e018f5c368c
|
afa666f94104aca6725fb837f7c037ba586c0fbd
|
Loading…
Reference in New Issue