mirror of https://github.com/axmolengine/axmol.git
add CCHttpRequest and related tests to repository
This commit is contained in:
parent
dc35596ce9
commit
24136dc575
|
@ -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 <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
|
||||
|
||||
|
|
@ -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<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;
|
||||
};
|
||||
|
||||
NS_CC_EXT_END
|
||||
|
||||
#endif //__CCHTTPREQUEST_H__
|
|
@ -28,5 +28,7 @@
|
|||
#include "GUI/CCControlExtension/CCControlExtensions.h"
|
||||
#include "GUI/CCScrollView/CCScrollView.h"
|
||||
|
||||
#include "Network/CCHttpRequest/CCHttpRequest.h"
|
||||
|
||||
#endif /* __COCOS2D_EXT_H__ */
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<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)
|
||||
{
|
||||
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 <iostream>
|
||||
|
|
|
@ -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);
|
||||
|
||||
#endif
|
||||
//Http Response Callback
|
||||
void onHttpRequestCompleted(cocos2d::CCObject *sender, void *data);
|
||||
};
|
||||
|
||||
void runHttpRequestTest();
|
||||
|
||||
#endif //__HTTPREQUESTHTTP_H
|
||||
|
|
|
@ -1 +1 @@
|
|||
7dea09f758e229595c7992fee3cc0a0f0fb49f79
|
||||
eae8f176936098bc77ca4472b94f9e018f5c368c
|
Loading…
Reference in New Issue