mirror of https://github.com/axmolengine/axmol.git
HttpClient: Fix crash when process redirect
This commit is contained in:
parent
af0a0f1598
commit
f4969ee388
|
@ -4,19 +4,19 @@
|
||||||
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
||||||
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
||||||
Copyright (c) 2021 Bytedance Inc.
|
Copyright (c) 2021 Bytedance Inc.
|
||||||
|
|
||||||
https://adxe.org
|
https://adxe.org
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
The above copyright notice and this permission notice shall be included in
|
||||||
all copies or substantial portions of the Software.
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
@ -42,37 +42,29 @@ namespace network {
|
||||||
|
|
||||||
static HttpClient* _httpClient = nullptr; // pointer to singleton
|
static HttpClient* _httpClient = nullptr; // pointer to singleton
|
||||||
|
|
||||||
template<typename _Cont, typename _Fty>
|
template <typename _Cont, typename _Fty>
|
||||||
static void __clearQueueUnsafe(_Cont& queue, _Fty pred) {
|
static void __clearQueueUnsafe(_Cont& queue, _Fty pred) {
|
||||||
for (auto it = queue.unsafe_begin(); it != queue.unsafe_end();)
|
for (auto it = queue.unsafe_begin(); it != queue.unsafe_end();) {
|
||||||
{
|
if (!pred || pred((*it))) {
|
||||||
if (!pred || pred((*it)))
|
|
||||||
{
|
|
||||||
(*it)->release();
|
(*it)->release();
|
||||||
it = queue.unsafe_erase(it);
|
it = queue.unsafe_erase(it);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HttpClient implementation
|
// HttpClient implementation
|
||||||
HttpClient* HttpClient::getInstance()
|
HttpClient* HttpClient::getInstance() {
|
||||||
{
|
if (_httpClient == nullptr) {
|
||||||
if (_httpClient == nullptr)
|
|
||||||
{
|
|
||||||
_httpClient = new (std::nothrow) HttpClient();
|
_httpClient = new (std::nothrow) HttpClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _httpClient;
|
return _httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::destroyInstance()
|
void HttpClient::destroyInstance() {
|
||||||
{
|
if (nullptr == _httpClient) {
|
||||||
if (nullptr == _httpClient)
|
|
||||||
{
|
|
||||||
CCLOG("HttpClient singleton is nullptr");
|
CCLOG("HttpClient singleton is nullptr");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -84,34 +76,24 @@ void HttpClient::destroyInstance()
|
||||||
CCLOG("HttpClient::destroyInstance() finished!");
|
CCLOG("HttpClient::destroyInstance() finished!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::enableCookies(const char* cookieFile)
|
void HttpClient::enableCookies(const char* cookieFile) {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_cookieFileMutex);
|
std::lock_guard<std::recursive_mutex> lock(_cookieFileMutex);
|
||||||
if (cookieFile)
|
if (cookieFile) {
|
||||||
{
|
|
||||||
_cookieFilename = std::string(cookieFile);
|
_cookieFilename = std::string(cookieFile);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
_cookieFilename = (FileUtils::getInstance()->getNativeWritableAbsolutePath() + "cookieFile.txt");
|
_cookieFilename = (FileUtils::getInstance()->getNativeWritableAbsolutePath() + "cookieFile.txt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::setSSLVerification(const std::string& caFile)
|
void HttpClient::setSSLVerification(const std::string& caFile) {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_sslCaFileMutex);
|
std::lock_guard<std::recursive_mutex> lock(_sslCaFileMutex);
|
||||||
_sslCaFilename = caFile;
|
_sslCaFilename = caFile;
|
||||||
_service->set_option(yasio::YOPT_S_SSL_CACERT, _sslCaFilename.c_str());
|
_service->set_option(yasio::YOPT_S_SSL_CACERT, _sslCaFilename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClient::HttpClient()
|
HttpClient::HttpClient()
|
||||||
: _isInited(false)
|
: _isInited(false), _dispatchOnWorkThread(false), _timeoutForConnect(30), _timeoutForRead(60), _cookie(nullptr),
|
||||||
, _dispatchOnWorkThread(false)
|
_clearResponsePredicate(nullptr) {
|
||||||
, _timeoutForConnect(30)
|
|
||||||
, _timeoutForRead(60)
|
|
||||||
, _cookie(nullptr)
|
|
||||||
, _clearResponsePredicate(nullptr)
|
|
||||||
{
|
|
||||||
CCLOG("In the constructor of HttpClient!");
|
CCLOG("In the constructor of HttpClient!");
|
||||||
_scheduler = Director::getInstance()->getScheduler();
|
_scheduler = Director::getInstance()->getScheduler();
|
||||||
|
|
||||||
|
@ -126,14 +108,12 @@ HttpClient::HttpClient()
|
||||||
_isInited = true;
|
_isInited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClient::~HttpClient()
|
HttpClient::~HttpClient() {
|
||||||
{
|
|
||||||
delete _service;
|
delete _service;
|
||||||
CCLOG("HttpClient destructor");
|
CCLOG("HttpClient destructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpClient::send(HttpRequest* request)
|
bool HttpClient::send(HttpRequest* request) {
|
||||||
{
|
|
||||||
if (!request)
|
if (!request)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -159,10 +139,12 @@ void HttpClient::processResponse(HttpResponse* response, const std::string& url)
|
||||||
|
|
||||||
if (channelIndex != -1) {
|
if (channelIndex != -1) {
|
||||||
if (response->prepareForProcess(url)) {
|
if (response->prepareForProcess(url)) {
|
||||||
auto& requestUri = response->getRequestUri();
|
response->_responseHeaders.clear(); // redirect needs clear old response headers
|
||||||
auto channelHandle = _service->channel_at(channelIndex);
|
auto& requestUri = response->getRequestUri();
|
||||||
|
auto channelHandle = _service->channel_at(channelIndex);
|
||||||
channelHandle->ud_.ptr = response;
|
channelHandle->ud_.ptr = response;
|
||||||
_service->set_option(YOPT_C_REMOTE_ENDPOINT, channelIndex, requestUri.getHost().c_str(), (int) requestUri.getPort());
|
_service->set_option(
|
||||||
|
YOPT_C_REMOTE_ENDPOINT, channelIndex, requestUri.getHost().c_str(), (int) requestUri.getPort());
|
||||||
if (requestUri.isSecure())
|
if (requestUri.isSecure())
|
||||||
_service->open(channelIndex, YCK_SSL_CLIENT);
|
_service->open(channelIndex, YCK_SSL_CLIENT);
|
||||||
else
|
else
|
||||||
|
@ -174,10 +156,10 @@ void HttpClient::processResponse(HttpResponse* response, const std::string& url)
|
||||||
_responseQueue.push_back(response);
|
_responseQueue.push_back(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::handleNetworkEvent(yasio::io_event* event) {
|
void HttpClient::handleNetworkEvent(yasio::io_event* event) {
|
||||||
int channelIndex = event->cindex();
|
int channelIndex = event->cindex();
|
||||||
auto channel = _service->channel_at(event->cindex());
|
auto channel = _service->channel_at(event->cindex());
|
||||||
HttpResponse* response = (HttpResponse*) channel->ud_.ptr;
|
HttpResponse* response = (HttpResponse*) channel->ud_.ptr;
|
||||||
if (!response)
|
if (!response)
|
||||||
return;
|
return;
|
||||||
|
@ -197,8 +179,8 @@ void HttpClient::handleNetworkEvent(yasio::io_event* event) {
|
||||||
if (event->status() == 0) {
|
if (event->status() == 0) {
|
||||||
obstream obs;
|
obstream obs;
|
||||||
bool usePostData = false;
|
bool usePostData = false;
|
||||||
auto request = response->getHttpRequest();
|
auto request = response->getHttpRequest();
|
||||||
switch (request->getRequestType()) {
|
switch (request->getRequestType()) {
|
||||||
case HttpRequest::Type::GET:
|
case HttpRequest::Type::GET:
|
||||||
obs.write_bytes("GET");
|
obs.write_bytes("GET");
|
||||||
break;
|
break;
|
||||||
|
@ -239,7 +221,7 @@ void HttpClient::handleNetworkEvent(yasio::io_event* event) {
|
||||||
|
|
||||||
bool userAgentSpecified = false;
|
bool userAgentSpecified = false;
|
||||||
bool contentTypeSpecified = false;
|
bool contentTypeSpecified = false;
|
||||||
if(!headers.empty()) {
|
if (!headers.empty()) {
|
||||||
using namespace cxx17; // for string_view literal
|
using namespace cxx17; // for string_view literal
|
||||||
for (auto& header : headers) {
|
for (auto& header : headers) {
|
||||||
obs.write_bytes(header);
|
obs.write_bytes(header);
|
||||||
|
@ -250,8 +232,8 @@ void HttpClient::handleNetworkEvent(yasio::io_event* event) {
|
||||||
else if (cxx20::ic::starts_with(cxx17::string_view{header}, "Content-Type:"_sv))
|
else if (cxx20::ic::starts_with(cxx17::string_view{header}, "Content-Type:"_sv))
|
||||||
contentTypeSpecified = true;
|
contentTypeSpecified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userAgentSpecified)
|
if (!userAgentSpecified)
|
||||||
obs.write_bytes("User-Agent: yasio-http\r\n");
|
obs.write_bytes("User-Agent: yasio-http\r\n");
|
||||||
|
|
||||||
|
@ -284,7 +266,7 @@ void HttpClient::handleNetworkEvent(yasio::io_event* event) {
|
||||||
response->updateInternalCode(yasio::errc::read_timeout);
|
response->updateInternalCode(yasio::errc::read_timeout);
|
||||||
s.close(channelIndex); // timeout
|
s.close(channelIndex); // timeout
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleNetworkEOF(response, channel, event->status());
|
handleNetworkEOF(response, channel, event->status());
|
||||||
}
|
}
|
||||||
|
@ -306,7 +288,7 @@ void HttpClient::handleNetworkEOF(HttpResponse* response, yasio::io_channel* cha
|
||||||
if (response->increaseRedirectCount() < HttpClient::MAX_REDIRECT_COUNT) {
|
if (response->increaseRedirectCount() < HttpClient::MAX_REDIRECT_COUNT) {
|
||||||
auto iter = response->_responseHeaders.find("LOCATION");
|
auto iter = response->_responseHeaders.find("LOCATION");
|
||||||
if (iter != response->_responseHeaders.end()) {
|
if (iter != response->_responseHeaders.end()) {
|
||||||
_availChannelQueue.push_back(channel->index());
|
_availChannelQueue.push_back(channel->index());
|
||||||
processResponse(response, iter->second);
|
processResponse(response, iter->second);
|
||||||
response->release();
|
response->release();
|
||||||
return;
|
return;
|
||||||
|
@ -317,7 +299,7 @@ void HttpClient::handleNetworkEOF(HttpResponse* response, yasio::io_channel* cha
|
||||||
finishResponse(response);
|
finishResponse(response);
|
||||||
|
|
||||||
// recycle channel
|
// recycle channel
|
||||||
_availChannelQueue.push_back(channel->index());
|
_availChannelQueue.push_back(channel->index());
|
||||||
|
|
||||||
// try process pending response
|
// try process pending response
|
||||||
auto lck = _responseQueue.get_lock();
|
auto lck = _responseQueue.get_lock();
|
||||||
|
@ -358,44 +340,37 @@ void HttpClient::clearResponseQueue() {
|
||||||
__clearQueueUnsafe(_responseQueue, ClearResponsePredicate{});
|
__clearQueueUnsafe(_responseQueue, ClearResponsePredicate{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::setTimeoutForConnect(int value)
|
void HttpClient::setTimeoutForConnect(int value) {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_timeoutForConnectMutex);
|
std::lock_guard<std::recursive_mutex> lock(_timeoutForConnectMutex);
|
||||||
_timeoutForConnect = value;
|
_timeoutForConnect = value;
|
||||||
_service->set_option(YOPT_S_CONNECT_TIMEOUT, value);
|
_service->set_option(YOPT_S_CONNECT_TIMEOUT, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpClient::getTimeoutForConnect()
|
int HttpClient::getTimeoutForConnect() {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_timeoutForConnectMutex);
|
std::lock_guard<std::recursive_mutex> lock(_timeoutForConnectMutex);
|
||||||
return _timeoutForConnect;
|
return _timeoutForConnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpClient::setTimeoutForRead(int value)
|
void HttpClient::setTimeoutForRead(int value) {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_timeoutForReadMutex);
|
std::lock_guard<std::recursive_mutex> lock(_timeoutForReadMutex);
|
||||||
_timeoutForRead = value;
|
_timeoutForRead = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpClient::getTimeoutForRead()
|
int HttpClient::getTimeoutForRead() {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_timeoutForReadMutex);
|
std::lock_guard<std::recursive_mutex> lock(_timeoutForReadMutex);
|
||||||
return _timeoutForRead;
|
return _timeoutForRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& HttpClient::getCookieFilename()
|
const std::string& HttpClient::getCookieFilename() {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_cookieFileMutex);
|
std::lock_guard<std::recursive_mutex> lock(_cookieFileMutex);
|
||||||
return _cookieFilename;
|
return _cookieFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& HttpClient::getSSLVerification()
|
const std::string& HttpClient::getSSLVerification() {
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(_sslCaFileMutex);
|
std::lock_guard<std::recursive_mutex> lock(_sslCaFileMutex);
|
||||||
return _sslCaFilename;
|
return _sslCaFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace network
|
||||||
|
|
||||||
NS_CC_END
|
NS_CC_END
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ public:
|
||||||
const std::string& getSSLVerification();
|
const std::string& getSSLVerification();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a get request to task queue
|
* Send http request concurrently, non-blocking
|
||||||
*
|
*
|
||||||
* @param request a HttpRequest object, which includes url, response callback etc.
|
* @param request a HttpRequest object, which includes url, response callback etc.
|
||||||
please make sure request->_requestData is clear before calling "send" here.
|
please make sure request->_requestData is clear before calling "send" here.
|
||||||
|
|
|
@ -173,7 +173,6 @@ private:
|
||||||
_finished = false;
|
_finished = false;
|
||||||
_responseData.clear();
|
_responseData.clear();
|
||||||
_currentHeader.clear();
|
_currentHeader.clear();
|
||||||
_responseHeaders.clear();
|
|
||||||
_responseCode = -1;
|
_responseCode = -1;
|
||||||
_internalCode = 0;
|
_internalCode = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue