2020-11-24 15:41:18 +01:00
|
|
|
#pragma once
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
#include <brynet/net/http/WebSocketFormat.hpp>
|
|
|
|
#include <cassert>
|
2020-11-24 15:41:18 +01:00
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
2021-04-30 16:10:14 +02:00
|
|
|
#include <string>
|
2020-11-24 15:41:18 +01:00
|
|
|
|
|
|
|
#include "http_parser.h"
|
|
|
|
|
|
|
|
namespace brynet { namespace net { namespace http {
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
class HttpService;
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
class HTTPParser
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using Ptr = std::shared_ptr<HTTPParser>;
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
explicit HTTPParser(http_parser_type parserType)
|
|
|
|
: mParserType(parserType)
|
|
|
|
{
|
|
|
|
mLastWasValue = true;
|
|
|
|
|
|
|
|
mIsUpgrade = false;
|
|
|
|
mIsWebSocket = false;
|
|
|
|
mIsKeepAlive = false;
|
|
|
|
mISCompleted = false;
|
|
|
|
mStatusCode = 0;
|
|
|
|
mWSFrameType = WebSocketFormat::WebSocketFrameType::ERROR_FRAME;
|
|
|
|
mSettings.on_status = sStatusHandle;
|
|
|
|
mSettings.on_body = sBodyHandle;
|
|
|
|
mSettings.on_url = sUrlHandle;
|
|
|
|
mSettings.on_header_field = sHeadField;
|
|
|
|
mSettings.on_header_value = sHeadValue;
|
|
|
|
mSettings.on_headers_complete = sHeadComplete;
|
|
|
|
mSettings.on_message_begin = sMessageBegin;
|
|
|
|
mSettings.on_message_complete = sMessageEnd;
|
|
|
|
mSettings.on_chunk_header = sChunkHeader;
|
|
|
|
mSettings.on_chunk_complete = sChunkComplete;
|
|
|
|
mParser.data = this;
|
|
|
|
|
|
|
|
http_parser_init(&mParser, mParserType);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~HTTPParser() = default;
|
|
|
|
|
|
|
|
bool isUpgrade() const
|
|
|
|
{
|
|
|
|
return mIsUpgrade;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
bool isWebSocket() const
|
|
|
|
{
|
|
|
|
return mIsWebSocket;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
bool isKeepAlive() const
|
|
|
|
{
|
|
|
|
return mIsKeepAlive;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
bool isCompleted() const
|
|
|
|
{
|
|
|
|
return mISCompleted;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
int method() const
|
|
|
|
{
|
|
|
|
// mMethod's value defined in http_method, such as HTTP_GET、HTTP_POST.
|
|
|
|
// if mMethod is -1, it's invalid.
|
|
|
|
return mMethod;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
const std::string& getPath() const
|
|
|
|
{
|
|
|
|
return mPath;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
const std::string& getQuery() const
|
|
|
|
{
|
|
|
|
return mQuery;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
const std::string& getStatus() const
|
|
|
|
{
|
|
|
|
return mStatus;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
int getStatusCode() const
|
|
|
|
{
|
|
|
|
return mStatusCode;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
bool hasEntry(const std::string& key,
|
|
|
|
const std::string& value) const
|
|
|
|
{
|
|
|
|
const auto it = mHeadValues.find(key);
|
|
|
|
return it != mHeadValues.end() && value == it->second;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
bool hasKey(const std::string& key) const
|
|
|
|
{
|
|
|
|
return mHeadValues.find(key) != mHeadValues.end();
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
const std::string& getValue(const std::string& key) const
|
|
|
|
{
|
|
|
|
const static std::string emptystr("");
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
auto it = mHeadValues.find(key);
|
|
|
|
if (it != mHeadValues.end())
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
2021-04-30 16:10:14 +02:00
|
|
|
return (*it).second;
|
2020-11-24 15:41:18 +01:00
|
|
|
}
|
2021-04-30 16:10:14 +02:00
|
|
|
else
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
2021-04-30 16:10:14 +02:00
|
|
|
return emptystr;
|
2020-11-24 15:41:18 +01:00
|
|
|
}
|
2021-04-30 16:10:14 +02:00
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
const std::string& getBody() const
|
|
|
|
{
|
|
|
|
return mBody;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
std::string& getWSCacheFrame()
|
|
|
|
{
|
|
|
|
return mWSCacheFrame;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
std::string& getWSParseString()
|
|
|
|
{
|
|
|
|
return mWSParsePayload;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
WebSocketFormat::WebSocketFrameType getWSFrameType() const
|
|
|
|
{
|
|
|
|
return mWSFrameType;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
void cacheWSFrameType(WebSocketFormat::WebSocketFrameType frameType)
|
|
|
|
{
|
|
|
|
mWSFrameType = frameType;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
private:
|
|
|
|
void clearParse()
|
|
|
|
{
|
|
|
|
mMethod = -1;
|
|
|
|
mIsUpgrade = false;
|
|
|
|
mIsWebSocket = false;
|
|
|
|
mISCompleted = false;
|
|
|
|
mLastWasValue = true;
|
|
|
|
mUrl.clear();
|
|
|
|
mQuery.clear();
|
|
|
|
mBody.clear();
|
|
|
|
mStatus.clear();
|
|
|
|
mCurrentField.clear();
|
|
|
|
mCurrentValue.clear();
|
|
|
|
mHeadValues.clear();
|
|
|
|
mPath.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t tryParse(const char* buffer, size_t len)
|
|
|
|
{
|
|
|
|
const size_t nparsed = http_parser_execute(&mParser, &mSettings, buffer, len);
|
|
|
|
if (mISCompleted)
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
2021-04-30 16:10:14 +02:00
|
|
|
mIsUpgrade = mParser.upgrade;
|
|
|
|
mIsWebSocket = mIsUpgrade && hasEntry("Upgrade", "websocket");
|
|
|
|
mIsKeepAlive = hasEntry("Connection", "Keep-Alive");
|
|
|
|
mMethod = mParser.method;
|
|
|
|
http_parser_init(&mParser, mParserType);
|
2020-11-24 15:41:18 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
return nparsed;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
private:
|
|
|
|
static int sChunkHeader(http_parser* hp)
|
|
|
|
{
|
|
|
|
(void) hp;
|
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sChunkComplete(http_parser* hp)
|
|
|
|
{
|
|
|
|
(void) hp;
|
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sMessageBegin(http_parser* hp)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
httpParser->clearParse();
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sMessageEnd(http_parser* hp)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
httpParser->mISCompleted = true;
|
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sHeadComplete(http_parser* hp)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
if (httpParser->mUrl.empty())
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
struct http_parser_url u;
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
const int result = http_parser_parse_url(httpParser->mUrl.data(),
|
|
|
|
httpParser->mUrl.size(),
|
|
|
|
0,
|
|
|
|
&u);
|
|
|
|
if (result != 0)
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
2021-04-30 16:10:14 +02:00
|
|
|
return -1;
|
2020-11-24 15:41:18 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
if (!(u.field_set & (1 << UF_PATH)))
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
2021-04-30 16:10:14 +02:00
|
|
|
fprintf(stderr,
|
|
|
|
"\n\n*** failed to parse PATH in URL %s ***\n\n",
|
|
|
|
httpParser->mUrl.c_str());
|
|
|
|
return -1;
|
2020-11-24 15:41:18 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
httpParser->mPath = std::string(
|
|
|
|
httpParser->mUrl.data() + u.field_data[UF_PATH].off,
|
|
|
|
u.field_data[UF_PATH].len);
|
|
|
|
if (u.field_set & (1 << UF_QUERY))
|
2020-11-24 15:41:18 +01:00
|
|
|
{
|
2021-04-30 16:10:14 +02:00
|
|
|
httpParser->mQuery = std::string(
|
|
|
|
httpParser->mUrl.data() + u.field_data[UF_QUERY].off,
|
|
|
|
u.field_data[UF_QUERY].len);
|
2020-11-24 15:41:18 +01:00
|
|
|
}
|
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sUrlHandle(http_parser* hp, const char* url, size_t length)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
httpParser->mUrl.append(url, length);
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sHeadValue(http_parser* hp, const char* at, size_t length)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
auto& value = httpParser->mHeadValues[httpParser->mCurrentField];
|
|
|
|
value.append(at, length);
|
|
|
|
httpParser->mLastWasValue = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sHeadField(http_parser* hp, const char* at, size_t length)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
if (httpParser->mLastWasValue)
|
|
|
|
{
|
|
|
|
httpParser->mCurrentField.clear();
|
|
|
|
}
|
|
|
|
httpParser->mCurrentField.append(at, length);
|
|
|
|
httpParser->mLastWasValue = false;
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sStatusHandle(http_parser* hp, const char* at, size_t length)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
httpParser->mStatus.append(at, length);
|
|
|
|
httpParser->mStatusCode = hp->status_code;
|
|
|
|
return 0;
|
|
|
|
}
|
2020-11-24 15:41:18 +01:00
|
|
|
|
2021-04-30 16:10:14 +02:00
|
|
|
static int sBodyHandle(http_parser* hp, const char* at, size_t length)
|
|
|
|
{
|
|
|
|
HTTPParser* httpParser = (HTTPParser*) hp->data;
|
|
|
|
httpParser->mBody.append(at, length);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
const http_parser_type mParserType;
|
|
|
|
http_parser mParser;
|
|
|
|
http_parser_settings mSettings;
|
|
|
|
|
|
|
|
int mMethod = -1;
|
|
|
|
bool mIsUpgrade = false;
|
|
|
|
bool mIsWebSocket = false;
|
|
|
|
bool mIsKeepAlive;
|
|
|
|
bool mISCompleted;
|
|
|
|
|
|
|
|
bool mLastWasValue;
|
|
|
|
std::string mCurrentField;
|
|
|
|
std::string mCurrentValue;
|
|
|
|
|
|
|
|
std::string mPath;
|
|
|
|
std::string mQuery;
|
|
|
|
std::string mStatus;
|
|
|
|
std::map<std::string, std::string> mHeadValues;
|
|
|
|
int mStatusCode;
|
|
|
|
|
|
|
|
std::string mUrl;
|
|
|
|
std::string mBody;
|
|
|
|
|
|
|
|
std::string mWSCacheFrame;
|
|
|
|
std::string mWSParsePayload;
|
|
|
|
WebSocketFormat::WebSocketFrameType mWSFrameType;
|
|
|
|
|
|
|
|
private:
|
|
|
|
friend class HttpService;
|
|
|
|
};
|
|
|
|
|
|
|
|
}}}// namespace brynet::net::http
|