2019-11-23 20:27:39 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2010-2012 cocos2d-x.org
|
|
|
|
Copyright (c) 2013-2016 Chukong Technologies Inc.
|
|
|
|
Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
|
2021-06-24 17:04:04 +08:00
|
|
|
Copyright (c) 2021 Bytedance Inc.
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-07-06 21:15:02 +08:00
|
|
|
https://adxe.org
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
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__
|
2021-06-24 17:04:04 +08:00
|
|
|
#include <ctype.h>
|
2021-07-18 23:20:22 +08:00
|
|
|
#include <map>
|
2021-06-24 17:04:04 +08:00
|
|
|
#include <unordered_map>
|
2019-11-23 20:27:39 +08:00
|
|
|
#include "network/HttpRequest.h"
|
2021-06-24 17:04:04 +08:00
|
|
|
#include "network/Uri.h"
|
2021-07-18 23:20:22 +08:00
|
|
|
#include "llhttp.h"
|
2019-11-23 20:27:39 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @addtogroup network
|
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
NS_CC_BEGIN
|
|
|
|
|
|
|
|
namespace network {
|
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
class HttpClient;
|
2019-11-23 20:27:39 +08:00
|
|
|
/**
|
|
|
|
* @brief defines the object which users will receive at onHttpCompleted(sender, HttpResponse) callback.
|
|
|
|
* Please refer to samples/TestCpp/Classes/ExtensionTest/NetworkTest/HttpClientTest.cpp as a sample.
|
|
|
|
* @since v2.0.2.
|
|
|
|
* @lua NA
|
|
|
|
*/
|
|
|
|
class CC_DLL HttpResponse : public cocos2d::Ref
|
|
|
|
{
|
2021-06-24 17:04:04 +08:00
|
|
|
friend class HttpClient;
|
2019-11-23 20:27:39 +08:00
|
|
|
public:
|
2021-07-18 23:20:22 +08:00
|
|
|
using ResponseHeaderMap = std::multimap<std::string, std::string>;
|
2021-06-24 17:04:04 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
/**
|
|
|
|
* Constructor, it's used by HttpClient internal, users don't need to create HttpResponse manually.
|
|
|
|
* @param request the corresponding HttpRequest which leads to this response.
|
|
|
|
*/
|
|
|
|
HttpResponse(HttpRequest* request)
|
|
|
|
: _pHttpRequest(request)
|
|
|
|
{
|
|
|
|
if (_pHttpRequest)
|
|
|
|
{
|
|
|
|
_pHttpRequest->retain();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor, it will be called in HttpClient internal.
|
|
|
|
* Users don't need to destruct HttpResponse object manually.
|
|
|
|
*/
|
|
|
|
virtual ~HttpResponse()
|
|
|
|
{
|
|
|
|
if (_pHttpRequest)
|
|
|
|
{
|
|
|
|
_pHttpRequest->release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Override autorelease method to prevent developers from calling it.
|
|
|
|
* If this method is called , it would trigger CCASSERT.
|
|
|
|
* @return cocos2d::Ref* always return nullptr.
|
|
|
|
*/
|
|
|
|
cocos2d::Ref* autorelease()
|
|
|
|
{
|
|
|
|
CCASSERT(false, "HttpResponse is used between network thread and ui thread \
|
|
|
|
therefore, autorelease is forbidden here");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// getters, will be called by users
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the corresponding HttpRequest object which leads to this response.
|
|
|
|
* There's no paired setter for it, because it's already set in class constructor
|
|
|
|
* @return HttpRequest* the corresponding HttpRequest object which leads to this response.
|
|
|
|
*/
|
|
|
|
HttpRequest* getHttpRequest() const
|
|
|
|
{
|
|
|
|
return _pHttpRequest;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the http response data.
|
|
|
|
* @return std::vector<char>* the pointer that point to the _responseData.
|
|
|
|
*/
|
|
|
|
std::vector<char>* getResponseData()
|
|
|
|
{
|
|
|
|
return &_responseData;
|
|
|
|
}
|
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
bool isSucceed() const {
|
|
|
|
return _responseCode == 200;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the http response code to judge whether response is successful or not.
|
|
|
|
* I know that you want to see the _responseCode is 200.
|
|
|
|
* If _responseCode is not 200, you should check the meaning for _responseCode by the net.
|
2021-09-02 18:30:20 +08:00
|
|
|
* @return int32_t the value of _responseCode
|
2019-11-23 20:27:39 +08:00
|
|
|
*/
|
2020-10-05 02:40:38 +08:00
|
|
|
int getResponseCode() const
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
|
|
|
return _responseCode;
|
|
|
|
}
|
|
|
|
|
2021-06-25 18:29:16 +08:00
|
|
|
/*
|
|
|
|
* The yasio error code, see yasio::errc
|
|
|
|
*/
|
2020-10-05 02:40:38 +08:00
|
|
|
int getInternalCode() const { return _internalCode; }
|
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
int getRedirectCount() const {
|
|
|
|
return _redirectCount;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
const ResponseHeaderMap& getResponseHeaders() const {
|
|
|
|
return _responseHeaders;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
private:
|
2021-06-25 18:29:16 +08:00
|
|
|
void updateInternalCode(int value) {
|
|
|
|
if (_internalCode == 0)
|
|
|
|
_internalCode = value;
|
|
|
|
}
|
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
/**
|
2021-06-24 17:04:04 +08:00
|
|
|
* To see if the http request is finished.
|
2019-11-23 20:27:39 +08:00
|
|
|
*/
|
2021-06-24 17:04:04 +08:00
|
|
|
bool isFinished() const {
|
|
|
|
return _finished;
|
2021-06-24 15:54:02 +08:00
|
|
|
}
|
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
void handleInput(const std::vector<char>& data) {
|
|
|
|
enum llhttp_errno err = llhttp_execute(&_context, data.data(), data.size());
|
|
|
|
if (err != HPE_OK) {
|
|
|
|
_finished = true;
|
|
|
|
}
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the response data by the string pointer and the defined size.
|
|
|
|
* @param value a string pointer that point to response data buffer.
|
|
|
|
* @param n the defined size that the response data buffer would be copied.
|
|
|
|
*/
|
2021-06-24 17:04:04 +08:00
|
|
|
bool prepareForProcess(const std::string& url)
|
2019-11-23 20:27:39 +08:00
|
|
|
{
|
2021-06-24 17:04:04 +08:00
|
|
|
/* Resets response status */
|
|
|
|
_finished = false;
|
|
|
|
_responseData.clear();
|
|
|
|
_currentHeader.clear();
|
|
|
|
_responseCode = -1;
|
|
|
|
_internalCode = 0;
|
|
|
|
|
|
|
|
Uri uri = Uri::parse(url);
|
|
|
|
if (!uri.isValid())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
_requestUri = std::move(uri);
|
|
|
|
|
|
|
|
/* Initialize user callbacks and settings */
|
|
|
|
llhttp_settings_init(&_contextSettings);
|
|
|
|
|
|
|
|
/* Initialize the parser in HTTP_BOTH mode, meaning that it will select between
|
|
|
|
* HTTP_REQUEST and HTTP_RESPONSE parsing automatically while reading the first
|
|
|
|
* input.
|
|
|
|
*/
|
|
|
|
llhttp_init(&_context, HTTP_RESPONSE, &_contextSettings);
|
|
|
|
|
|
|
|
_context.data = this;
|
|
|
|
|
|
|
|
/* Set user callbacks */
|
2021-07-18 23:20:22 +08:00
|
|
|
_contextSettings.on_header_field = on_header_field;
|
|
|
|
_contextSettings.on_header_field_complete = on_header_field_complete;
|
|
|
|
_contextSettings.on_header_value = on_header_value;
|
|
|
|
_contextSettings.on_header_value_complete = on_header_value_complete;
|
|
|
|
_contextSettings.on_body = on_body;
|
|
|
|
_contextSettings.on_message_complete = on_complete;
|
2021-06-24 17:04:04 +08:00
|
|
|
|
|
|
|
return true;
|
2021-06-24 12:33:07 +08:00
|
|
|
}
|
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
const Uri& getRequestUri() const {
|
|
|
|
return _requestUri;
|
|
|
|
}
|
|
|
|
|
|
|
|
int increaseRedirectCount() {
|
|
|
|
return ++_redirectCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int on_header_field(llhttp_t* context, const char* at, size_t length) {
|
|
|
|
auto thiz = (HttpResponse*) context->data;
|
2021-07-18 23:20:22 +08:00
|
|
|
thiz->_currentHeader.insert(thiz->_currentHeader.end(), at, at + length);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int on_header_field_complete(llhttp_t* context) {
|
|
|
|
auto thiz = (HttpResponse*)context->data;
|
2021-07-19 08:04:12 +08:00
|
|
|
std::transform(thiz->_currentHeader.begin(), thiz->_currentHeader.end(), thiz->_currentHeader.begin(), ::tolower);
|
2021-06-24 17:04:04 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int on_header_value(llhttp_t* context, const char* at, size_t length) {
|
|
|
|
auto thiz = (HttpResponse*) context->data;
|
2021-07-18 23:20:22 +08:00
|
|
|
thiz->_currentHeaderValue.insert(thiz->_currentHeaderValue.end(), at, at + length);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int on_header_value_complete(llhttp_t* context) {
|
|
|
|
auto thiz = (HttpResponse*)context->data;
|
|
|
|
thiz->_responseHeaders.emplace(std::move(thiz->_currentHeader), std::move(thiz->_currentHeaderValue));
|
2021-06-24 17:04:04 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int on_body(llhttp_t* context, const char* at, size_t length) {
|
|
|
|
auto thiz = (HttpResponse*) context->data;
|
|
|
|
thiz->_responseData.insert(thiz->_responseData.end(), at, at + length);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int on_complete(llhttp_t* context) {
|
|
|
|
auto thiz = (HttpResponse*) context->data;
|
|
|
|
thiz->_responseCode = context->status_code;
|
|
|
|
thiz->_finished = true;
|
|
|
|
return 0;
|
2019-11-23 20:27:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2021-06-24 17:04:04 +08:00
|
|
|
|
2019-11-23 20:27:39 +08:00
|
|
|
// properties
|
|
|
|
HttpRequest* _pHttpRequest; /// the corresponding HttpRequest pointer who leads to this response
|
2021-06-24 17:04:04 +08:00
|
|
|
int _redirectCount = 0;
|
2021-06-24 15:54:02 +08:00
|
|
|
|
2021-06-24 17:04:04 +08:00
|
|
|
Uri _requestUri;
|
|
|
|
bool _finished = false; /// to indicate if the http request is successful simply
|
|
|
|
std::vector<char> _responseData; /// the returned raw data. You can also dump it as a string
|
|
|
|
std::string _currentHeader;
|
2021-07-18 23:20:22 +08:00
|
|
|
std::string _currentHeaderValue;
|
2021-06-24 17:04:04 +08:00
|
|
|
ResponseHeaderMap _responseHeaders; /// the returned raw header data. You can also dump it as a string
|
|
|
|
int _responseCode = -1; /// the status code returned from libcurl, e.g. 200, 404
|
|
|
|
int _internalCode = 0; /// the ret code of perform
|
|
|
|
llhttp_t _context;
|
|
|
|
llhttp_settings_t _contextSettings;
|
2019-11-23 20:27:39 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_CC_END
|
|
|
|
|
|
|
|
// end group
|
|
|
|
/// @}
|
|
|
|
|
|
|
|
#endif //__HTTP_RESPONSE_H__
|
|
|
|
|