Support notify progression in XMLHTTPRequest (#17509)

* Support notify progression in XMLHTTPRequest

* Fix indent

* Fix issues

* fix issue
This commit is contained in:
pandamicro 2017-03-16 14:09:51 +08:00 committed by minggo
parent 3ec14488fc
commit 0e0987d337
3 changed files with 207 additions and 186 deletions

View File

@ -78,10 +78,10 @@ static int processDeleteTask(HttpClient* client, HttpRequest* request, write_ca
// Worker thread
void HttpClient::networkThread()
{
increaseThreadCount();
while (true)
{
increaseThreadCount();
while (true)
{
HttpRequest *request;
@ -89,7 +89,7 @@ void HttpClient::networkThread()
{
std::lock_guard<std::mutex> lock(_requestQueueMutex);
while (_requestQueue.empty())
{
{
_sleepCondition.wait(_requestQueueMutex);
}
request = _requestQueue.at(0);
@ -104,67 +104,67 @@ void HttpClient::networkThread()
// Create a HttpResponse object, the default setting is http access failed
HttpResponse *response = new (std::nothrow) HttpResponse(request);
processResponse(response, _responseMessage);
processResponse(response, _responseMessage);
// add response packet into queue
_responseQueueMutex.lock();
_responseQueue.pushBack(response);
_responseQueueMutex.unlock();
_schedulerMutex.lock();
if (nullptr != _scheduler)
{
_scheduler->performFunctionInCocosThread(CC_CALLBACK_0(HttpClient::dispatchResponseCallbacks, this));
}
_schedulerMutex.unlock();
_schedulerMutex.lock();
if (nullptr != _scheduler)
{
_scheduler->performFunctionInCocosThread(CC_CALLBACK_0(HttpClient::dispatchResponseCallbacks, this));
}
_schedulerMutex.unlock();
}
// cleanup: if worker thread received quit signal, clean up un-completed request queue
_requestQueueMutex.lock();
_requestQueue.clear();
_requestQueueMutex.unlock();
_responseQueueMutex.lock();
_responseQueue.clear();
_responseQueueMutex.unlock();
decreaseThreadCountAndMayDeleteThis();
_responseQueueMutex.lock();
_responseQueue.clear();
_responseQueueMutex.unlock();
decreaseThreadCountAndMayDeleteThis();
}
// Worker thread
void HttpClient::networkThreadAlone(HttpRequest* request, HttpResponse* response)
{
increaseThreadCount();
increaseThreadCount();
char responseMessage[RESPONSE_BUFFER_SIZE] = { 0 };
processResponse(response, responseMessage);
_schedulerMutex.lock();
if (nullptr != _scheduler)
{
_scheduler->performFunctionInCocosThread([this, response, request]{
const ccHttpRequestCallback& callback = request->getCallback();
Ref* pTarget = request->getTarget();
SEL_HttpResponse pSelector = request->getSelector();
char responseMessage[RESPONSE_BUFFER_SIZE] = { 0 };
processResponse(response, responseMessage);
if (callback != nullptr)
{
callback(this, response);
}
else if (pTarget && pSelector)
{
(pTarget->*pSelector)(this, response);
}
response->release();
// do not release in other thread
request->release();
});
}
_schedulerMutex.unlock();
_schedulerMutex.lock();
if (nullptr != _scheduler)
{
_scheduler->performFunctionInCocosThread([this, response, request]{
const ccHttpRequestCallback& callback = request->getCallback();
Ref* pTarget = request->getTarget();
SEL_HttpResponse pSelector = request->getSelector();
decreaseThreadCountAndMayDeleteThis();
if (callback != nullptr)
{
callback(this, response);
}
else if (pTarget && pSelector)
{
(pTarget->*pSelector)(this, response);
}
response->release();
// do not release in other thread
request->release();
});
}
_schedulerMutex.unlock();
decreaseThreadCountAndMayDeleteThis();
}
//Configure curl's timeout property
@ -188,14 +188,14 @@ static bool configureCURL(HttpClient* client, CURL* handle, char* errorBuffer)
return false;
}
std::string sslCaFilename = client->getSSLVerification();
if (sslCaFilename.empty()) {
std::string sslCaFilename = client->getSSLVerification();
if (sslCaFilename.empty()) {
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 0L);
} else {
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1L);
curl_easy_setopt(handle, CURLOPT_SSL_VERIFYHOST, 2L);
curl_easy_setopt(handle, CURLOPT_CAINFO, sslCaFilename.c_str());
curl_easy_setopt(handle, CURLOPT_CAINFO, sslCaFilename.c_str());
}
// FIXED #3224: The subthread of CCHttpClient interrupts main thread if timeout comes.
@ -245,7 +245,7 @@ public:
{
if (!_curl)
return false;
if (!configureCURL(client, _curl, errorBuffer))
if (!configureCURL(client, _curl, errorBuffer))
return false;
/* get custom header data (if set) */
@ -259,12 +259,12 @@ public:
if (!setOption(CURLOPT_HTTPHEADER, _headers))
return false;
}
std::string cookieFilename = client->getCookieFilename();
if (!cookieFilename.empty()) {
if (!setOption(CURLOPT_COOKIEFILE, cookieFilename.c_str())) {
std::string cookieFilename = client->getCookieFilename();
if (!cookieFilename.empty()) {
if (!setOption(CURLOPT_COOKIEFILE, cookieFilename.c_str())) {
return false;
}
if (!setOption(CURLOPT_COOKIEJAR, cookieFilename.c_str())) {
if (!setOption(CURLOPT_COOKIEJAR, cookieFilename.c_str())) {
return false;
}
}
@ -297,7 +297,7 @@ public:
static int processGetTask(HttpClient* client, HttpRequest* request, write_callback callback, void* stream, long* responseCode, write_callback headerCallback, void* headerStream, char* errorBuffer)
{
CURLRaii curl;
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
&& curl.setOption(CURLOPT_FOLLOWLOCATION, true)
&& curl.perform(responseCode);
return ok ? 0 : 1;
@ -307,7 +307,7 @@ static int processGetTask(HttpClient* client, HttpRequest* request, write_callba
static int processPostTask(HttpClient* client, HttpRequest* request, write_callback callback, void* stream, long* responseCode, write_callback headerCallback, void* headerStream, char* errorBuffer)
{
CURLRaii curl;
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
&& curl.setOption(CURLOPT_POST, 1)
&& curl.setOption(CURLOPT_POSTFIELDS, request->getRequestData())
&& curl.setOption(CURLOPT_POSTFIELDSIZE, request->getRequestDataSize())
@ -319,7 +319,7 @@ static int processPostTask(HttpClient* client, HttpRequest* request, write_callb
static int processPutTask(HttpClient* client, HttpRequest* request, write_callback callback, void* stream, long* responseCode, write_callback headerCallback, void* headerStream, char* errorBuffer)
{
CURLRaii curl;
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
&& curl.setOption(CURLOPT_CUSTOMREQUEST, "PUT")
&& curl.setOption(CURLOPT_POSTFIELDS, request->getRequestData())
&& curl.setOption(CURLOPT_POSTFIELDSIZE, request->getRequestDataSize())
@ -331,7 +331,7 @@ static int processPutTask(HttpClient* client, HttpRequest* request, write_callba
static int processDeleteTask(HttpClient* client, HttpRequest* request, write_callback callback, void* stream, long* responseCode, write_callback headerCallback, void* headerStream, char* errorBuffer)
{
CURLRaii curl;
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
bool ok = curl.init(client, request, callback, stream, headerCallback, headerStream, errorBuffer)
&& curl.setOption(CURLOPT_CUSTOMREQUEST, "DELETE")
&& curl.setOption(CURLOPT_FOLLOWLOCATION, true)
&& curl.perform(responseCode);
@ -341,8 +341,8 @@ static int processDeleteTask(HttpClient* client, HttpRequest* request, write_cal
// HttpClient implementation
HttpClient* HttpClient::getInstance()
{
if (_httpClient == nullptr)
{
if (_httpClient == nullptr)
{
_httpClient = new (std::nothrow) HttpClient();
}
@ -351,29 +351,29 @@ HttpClient* HttpClient::getInstance()
void HttpClient::destroyInstance()
{
if (nullptr == _httpClient)
{
CCLOG("HttpClient singleton is nullptr");
return;
}
if (nullptr == _httpClient)
{
CCLOG("HttpClient singleton is nullptr");
return;
}
CCLOG("HttpClient::destroyInstance begin");
auto thiz = _httpClient;
_httpClient = nullptr;
CCLOG("HttpClient::destroyInstance begin");
auto thiz = _httpClient;
_httpClient = nullptr;
thiz->_scheduler->unscheduleAllForTarget(thiz);
thiz->_schedulerMutex.lock();
thiz->_scheduler = nullptr;
thiz->_schedulerMutex.unlock();
thiz->_scheduler->unscheduleAllForTarget(thiz);
thiz->_schedulerMutex.lock();
thiz->_scheduler = nullptr;
thiz->_schedulerMutex.unlock();
thiz->_requestQueueMutex.lock();
thiz->_requestQueue.pushBack(thiz->_requestSentinel);
thiz->_requestQueueMutex.unlock();
thiz->_requestQueueMutex.lock();
thiz->_requestQueue.pushBack(thiz->_requestSentinel);
thiz->_requestQueueMutex.unlock();
thiz->_sleepCondition.notify_one();
thiz->decreaseThreadCountAndMayDeleteThis();
thiz->_sleepCondition.notify_one();
thiz->decreaseThreadCountAndMayDeleteThis();
CCLOG("HttpClient::destroyInstance() finished!");
CCLOG("HttpClient::destroyInstance() finished!");
}
void HttpClient::enableCookies(const char* cookieFile)
@ -403,30 +403,30 @@ HttpClient::HttpClient()
, _cookie(nullptr)
, _requestSentinel(new HttpRequest())
{
CCLOG("In the constructor of HttpClient!");
memset(_responseMessage, 0, RESPONSE_BUFFER_SIZE * sizeof(char));
_scheduler = Director::getInstance()->getScheduler();
increaseThreadCount();
CCLOG("In the constructor of HttpClient!");
memset(_responseMessage, 0, RESPONSE_BUFFER_SIZE * sizeof(char));
_scheduler = Director::getInstance()->getScheduler();
increaseThreadCount();
}
HttpClient::~HttpClient()
{
CC_SAFE_RELEASE(_requestSentinel);
CCLOG("HttpClient destructor");
CC_SAFE_RELEASE(_requestSentinel);
CCLOG("HttpClient destructor");
}
//Lazy create semaphore & mutex & thread
bool HttpClient::lazyInitThreadSemaphore()
{
if (_isInited)
{
{
return true;
}
else
{
}
else
{
auto t = std::thread(CC_CALLBACK_0(HttpClient::networkThread, this));
t.detach();
_isInited = true;
_isInited = true;
}
return true;
@ -434,8 +434,8 @@ bool HttpClient::lazyInitThreadSemaphore()
//Add a get task to queue
void HttpClient::send(HttpRequest* request)
{
if (false == lazyInitThreadSemaphore())
{
if (false == lazyInitThreadSemaphore())
{
return;
}
@ -447,12 +447,12 @@ void HttpClient::send(HttpRequest* request)
request->retain();
_requestQueueMutex.lock();
_requestQueue.pushBack(request);
_requestQueueMutex.unlock();
_requestQueueMutex.lock();
_requestQueue.pushBack(request);
_requestQueueMutex.unlock();
// Notify thread start to work
_sleepCondition.notify_one();
// Notify thread start to work
_sleepCondition.notify_one();
}
void HttpClient::sendImmediate(HttpRequest* request)
@ -510,93 +510,93 @@ void HttpClient::dispatchResponseCallbacks()
// Process Response
void HttpClient::processResponse(HttpResponse* response, char* responseMessage)
{
auto request = response->getHttpRequest();
long responseCode = -1;
int retValue = 0;
auto request = response->getHttpRequest();
long responseCode = -1;
int retValue = 0;
// Process the request -> get response packet
switch (request->getRequestType())
{
case HttpRequest::Type::GET: // HTTP GET
retValue = processGetTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
// Process the request -> get response packet
switch (request->getRequestType())
{
case HttpRequest::Type::GET: // HTTP GET
retValue = processGetTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
case HttpRequest::Type::POST: // HTTP POST
retValue = processPostTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
case HttpRequest::Type::POST: // HTTP POST
retValue = processPostTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
case HttpRequest::Type::PUT:
retValue = processPutTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
case HttpRequest::Type::PUT:
retValue = processPutTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
case HttpRequest::Type::DELETE:
retValue = processDeleteTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
case HttpRequest::Type::DELETE:
retValue = processDeleteTask(this, request,
writeData,
response->getResponseData(),
&responseCode,
writeHeaderData,
response->getResponseHeader(),
responseMessage);
break;
default:
CCASSERT(false, "CCHttpClient: unknown request type, only GET,POST,PUT or DELETE is supported");
break;
}
default:
CCASSERT(false, "CCHttpClient: unknown request type, only GET, POST, PUT or DELETE is supported");
break;
}
// write data to HttpResponse
response->setResponseCode(responseCode);
if (retValue != 0)
{
response->setSucceed(false);
response->setErrorBuffer(responseMessage);
}
else
{
response->setSucceed(true);
}
// write data to HttpResponse
response->setResponseCode(responseCode);
if (retValue != 0)
{
response->setSucceed(false);
response->setErrorBuffer(responseMessage);
}
else
{
response->setSucceed(true);
}
}
void HttpClient::increaseThreadCount()
{
_threadCountMutex.lock();
++_threadCount;
_threadCountMutex.unlock();
_threadCountMutex.lock();
++_threadCount;
_threadCountMutex.unlock();
}
void HttpClient::decreaseThreadCountAndMayDeleteThis()
{
bool needDeleteThis = false;
_threadCountMutex.lock();
--_threadCount;
if (0 == _threadCount)
{
needDeleteThis = true;
}
bool needDeleteThis = false;
_threadCountMutex.lock();
--_threadCount;
if (0 == _threadCount)
{
needDeleteThis = true;
}
_threadCountMutex.unlock();
if (needDeleteThis)
{
delete this;
}
_threadCountMutex.unlock();
if (needDeleteThis)
{
delete this;
}
}
void HttpClient::setTimeoutForConnect(int value)

View File

@ -200,33 +200,53 @@ void MinXmlHttpRequest::handle_requestResponse(cocos2d::network::HttpClient *sen
return;
}
if (0 != strlen(response->getHttpRequest()->getTag()))
std::string tag = response->getHttpRequest()->getTag();
if (!tag.empty())
{
CCLOG("%s completed", response->getHttpRequest()->getTag());
CCLOG("%s completed", tag.c_str());
}
long statusCode = response->getResponseCode();
char statusString[64] = {0};
sprintf(statusString, "HTTP Status Code: %ld, tag = %s", statusCode, response->getHttpRequest()->getTag());
sprintf(statusString, "HTTP Status Code: %ld, tag = %s", statusCode, tag.c_str());
if (!response->isSucceed())
{
CCLOG("Response failed, error buffer: %s", response->getErrorBuffer());
std::string errorBuffer = response->getErrorBuffer();
CCLOG("Response failed, error buffer: %s", errorBuffer.c_str());
if (statusCode == 0 || statusCode == -1)
{
_errorFlag = true;
_status = 0;
_statusText.clear();
JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET
JS::RootedObject callback(_cx);
if (_onerrorCallback)
{
JS::RootedObject errorObj(_cx, JS_NewObject(_cx, NULL, JS::NullPtr(), JS::NullPtr()));
// event type
JS::RootedValue value(_cx, std_string_to_jsval(_cx, "error"));
JS_SetProperty(_cx, errorObj, "type", value);
// status
value.set(long_to_jsval(_cx, statusCode));
JS_SetProperty(_cx, errorObj, "status", value);
// tag
value.set(std_string_to_jsval(_cx, tag));
JS_SetProperty(_cx, errorObj, "tag", value);
// errorBuffer
value.set(std_string_to_jsval(_cx, errorBuffer));
JS_SetProperty(_cx, errorObj, "errorBuffer", value);
JS::RootedValue arg(_cx, OBJECT_TO_JSVAL(errorObj));
JS::HandleValueArray args(arg);
callback.set(_onerrorCallback);
_notify(callback);
_notify(callback, args);
}
if (_onloadendCallback)
{
callback.set(_onloadendCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
return;
}
@ -259,17 +279,17 @@ void MinXmlHttpRequest::handle_requestResponse(cocos2d::network::HttpClient *sen
if (_onreadystateCallback)
{
callback.set(_onreadystateCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
if (_onloadCallback)
{
callback.set(_onloadCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
if (_onloadendCallback)
{
callback.set(_onloadendCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
}
/**
@ -400,6 +420,7 @@ JS_BINDED_CONSTRUCTOR_IMPL(MinXmlHttpRequest)
js_proxy_t *p = jsb_new_proxy(req, obj);
#if CC_ENABLE_GC_FOR_NATIVE_OBJECTS
CC_UNUSED_PARAM(p);
js_add_FinalizeHook(cx, obj, true);
// don't retain it, already retained
#if COCOS2D_DEBUG > 1
@ -835,8 +856,8 @@ JS_BINDED_FUNC_IMPL(MinXmlHttpRequest, send)
return false;
}
}
else if(args.get(0).isNullOrUndefined()){
}
else if (args.get(0).isNullOrUndefined())
{}
else
{
return false;
@ -848,7 +869,7 @@ JS_BINDED_FUNC_IMPL(MinXmlHttpRequest, send)
if (_onloadstartCallback)
{
JS::RootedObject callback(cx, _onloadstartCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
//begin schedule for timeout
@ -868,7 +889,7 @@ void MinXmlHttpRequest::update(float dt)
if (_ontimeoutCallback)
{
JS::RootedObject callback(_cx, _ontimeoutCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
_elapsedTime = 0;
_readyState = UNSENT;
@ -894,7 +915,7 @@ JS_BINDED_FUNC_IMPL(MinXmlHttpRequest, abort)
if (_onabortCallback)
{
JS::RootedObject callback(cx, _onabortCallback);
_notify(callback);
_notify(callback, JS::HandleValueArray::empty());
}
return true;
@ -1020,7 +1041,7 @@ static void basic_object_finalize(JSFreeOp *freeOp, JSObject *obj)
CCLOG("basic_object_finalize %p ...", obj);
}
void MinXmlHttpRequest::_notify(JS::HandleObject callback)
void MinXmlHttpRequest::_notify(JS::HandleObject callback, JS::HandleValueArray args)
{
js_proxy_t * p;
void* ptr = (void*)this;
@ -1035,7 +1056,7 @@ void MinXmlHttpRequest::_notify(JS::HandleObject callback)
//JS_IsExceptionPending(cx) && JS_ReportPendingException(cx);
JS::RootedValue callbackVal(_cx, OBJECT_TO_JSVAL(callback));
JS::RootedValue out(_cx);
JS_CallFunctionValue(_cx, JS::NullPtr(), callbackVal, JS::HandleValueArray::empty(), &out);
JS_CallFunctionValue(_cx, JS::NullPtr(), callbackVal, args, &out);
}
}

View File

@ -95,8 +95,8 @@ private:
void _setHttpRequestHeader();
void _setHttpRequestData(const char *data, size_t len);
void _sendRequest(JSContext *cx);
void _notify(JS::HandleObject callback);
void _notify(JS::HandleObject callback, JS::HandleValueArray args);
std::string _url;
JSContext* _cx;
std::string _meth;