HP-Socket/Windows/Src/HttpHelper.h
2022-01-13 22:59:11 +08:00

1375 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
*
* Author : Bruce Liang
* Website : https://github.com/ldcsaa
* Project : https://github.com/ldcsaa/HP-Socket
* Blog : http://www.cnblogs.com/ldcsaa
* Wiki : http://www.oschina.net/p/hp-socket
* QQ Group : 44636872, 75375912
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "../Include/HPSocket/HPTypeDef.h"
#ifdef _HTTP_SUPPORT
#include "SocketHelper.h"
#include "HttpCookie.h"
#pragma warning(push)
#pragma warning(disable: 4005)
#include "Common/http/llhttp.h"
#include "Common/http/llhttp_url.h"
#pragma warning(pop)
typedef llhttp_t http_parser;
typedef llhttp_settings_t http_parser_settings;
#define LLHTTP_MAX_ERROR_NUM 23
#define LLHTTP_MAX_METHOD_NUM 45
/************************************************************************
名称HTTP 全局常量
描述:声明 HTTP 组件的公共全局常量
************************************************************************/
#define HTTP_DEFAULT_PORT 80
#define HTTPS_DEFAULT_PORT 443
#define HTTP_SCHEMA "http://"
#define HTTPS_SCHEMA "https://"
#define HTTP_CRLF "\r\n"
#define HTTP_PATH_SEPARATOR_CHAR '/'
#define HTTP_PATH_SEPARATOR "/"
#define HTTP_HEADER_SEPARATOR ": "
#define HTTP_COOKIE_SEPARATOR "; "
#define HTTP_1_0_STR "HTTP/1.0"
#define HTTP_1_1_STR "HTTP/1.1"
#define HTTP_HEADER_HOST "Host"
#define HTTP_HEADER_COOKIE "Cookie"
#define HTTP_HEADER_SET_COOKIE "Set-Cookie"
#define HTTP_HEADER_CONTENT_TYPE "Content-Type"
#define HTTP_HEADER_CONTENT_LENGTH "Content-Length"
#define HTTP_HEADER_CONTENT_ENCODING "Content-Encoding"
#define HTTP_HEADER_TRANSFER_ENCODING "Transfer-Encoding"
#define HTTP_HEADER_CONNECTION "Connection"
#define HTTP_HEADER_UPGRADE "Upgrade"
#define HTTP_HEADER_VALUE_WEB_SOCKET "WebSocket"
#define HTTP_CONNECTION_CLOSE_VALUE "close"
#define HTTP_CONNECTION_KEEPALIVE_VALUE "keep-alive"
#define HTTP_METHOD_POST "POST"
#define HTTP_METHOD_PUT "PUT"
#define HTTP_METHOD_PATCH "PATCH"
#define HTTP_METHOD_GET "GET"
#define HTTP_METHOD_DELETE "DELETE"
#define HTTP_METHOD_HEAD "HEAD"
#define HTTP_METHOD_TRACE "TRACE"
#define HTTP_METHOD_OPTIONS "OPTIONS"
#define HTTP_METHOD_CONNECT "CONNECT"
#define HTTP_MIN_WS_HEADER_LEN 2
#define HTTP_MAX_WS_HEADER_LEN 14
#define MIN_HTTP_RELEASE_CHECK_INTERVAL 1000
#define MIN_HTTP_RELEASE_DELAY 100
#define MAX_HTTP_RELEASE_DELAY (60 * 1000)
#define DEFAULT_HTTP_RELEASE_DELAY (3 * 1000)
#define DEFAULT_HTTP_VERSION HV_1_1
#define DEFAULT_HTTP_SYNC_CONNECT_TIMEOUT 5000
#define DEFAULT_HTTP_SYNC_REQUEST_TIMEOUT 10000
// ------------------------------------------------------------------------------------------------------------- //
enum EnHttpSyncRequestProgress
{
HSRP_DONE,
HSRP_WAITING,
HSRP_ERROR,
HSRP_CLOSE
};
struct TDyingConnection
{
CONNID connID;
DWORD killTime;
TDyingConnection(CONNID id, DWORD kt = 0)
: connID (id)
, killTime (kt == 0 ? ::TimeGetTime() : kt)
{
}
static TDyingConnection* Construct(CONNID id, DWORD kt = 0) {return new TDyingConnection(id, kt);}
static void Destruct(TDyingConnection* pObj) {if(pObj) delete pObj;}
};
typedef unordered_multimap<CStringA, CStringA,
cstringa_nc_hash_func::hash, cstringa_nc_hash_func::equal_to> THeaderMap;
typedef THeaderMap::const_iterator THeaderMapCI;
typedef THeaderMap::iterator THeaderMapI;
typedef unordered_map<CStringA, CStringA,
cstringa_hash_func::hash, cstringa_hash_func::equal_to> TCookieMap;
typedef TCookieMap::const_iterator TCookieMapCI;
typedef TCookieMap::iterator TCookieMapI;
// ------------------------------------------------------------------------------------------------------------- //
struct TBaseWSHeader
{
public:
BOOL fin()
{
return (data >> 7) & 0x1;
}
void set_fin(BOOL v)
{
data |= ((v ? 1 : 0) << 7);
}
BYTE rsv()
{
return (data >> 4) & 0x7;
}
void set_rsv(BYTE v)
{
data |= ((v & 0x7) << 4);
}
BYTE code()
{
return data & 0xF;
}
void set_code(BYTE v)
{
data |= (v & 0xF);
}
BOOL mask()
{
return (data >> 15) & 0x1;
}
void set_mask(BOOL v)
{
data |= ((v ? 1 : 0) << 15);
}
BYTE len()
{
return (data >> 8) & 0x7F;
}
void set_len(BYTE v)
{
data |= ((v & 0x7F) << 8);
}
USHORT extlen()
{
return ntohs(data >> 16);
}
void set_extlen(USHORT v)
{
data |= (htons(v) << 16);
}
TBaseWSHeader(const BYTE* p, BOOL bZero = FALSE)
: data(*(UINT*)p)
{
if(bZero) data = 0;
}
private:
UINT& data;
};
template<class T> struct TWSContext
{
public:
EnHandleResult Parse(const BYTE* pData, int iLength)
{
ASSERT(pData != nullptr && iLength > 0);
EnHandleResult hr = HR_OK;
BYTE* pTemp = (BYTE*)pData;
int iRemain = iLength;
int iMin = 0;
while(iRemain > 0)
{
if(m_bHeader)
{
iMin = min(m_iHeaderRemain, iRemain);
memcpy(m_szHeader + m_iHeaderLen - m_iHeaderRemain, pTemp, iMin);
m_iHeaderRemain -= iMin;
if(m_iHeaderRemain == 0)
{
TBaseWSHeader bh(m_szHeader);
int iLen = bh.len();
int iExtLen = iLen < 126 ? 0 : (iLen == 126 ? 2 : 8);
int iMaskLen = bh.mask() ? 4 : 0;
int iRealHeaderLen = HTTP_MIN_WS_HEADER_LEN + iExtLen + iMaskLen;
if(m_iHeaderLen < iRealHeaderLen)
{
m_iHeaderRemain = iRealHeaderLen - m_iHeaderLen;
m_iHeaderLen = iRealHeaderLen;
}
else
{
if((m_pHttpObj->IsRequest() && iMaskLen == 0) || (!m_pHttpObj->IsRequest() && iMaskLen > 0))
{
::SetLastError(ERROR_INVALID_DATA);
hr = HR_ERROR;
break;
}
m_ullBodyLen = iExtLen == 0 ? iLen : (iExtLen == 2 ? bh.extlen() : NToH64(*(ULONGLONG*)(m_szHeader + HTTP_MIN_WS_HEADER_LEN)));
m_ullBodyRemain = m_ullBodyLen;
m_lpszMask = iMaskLen > 0 ? m_szHeader + HTTP_MIN_WS_HEADER_LEN + iExtLen : nullptr;
hr = m_pHttpObj->on_ws_message_header(bh.fin(), bh.rsv(), bh.code(), m_lpszMask, m_ullBodyLen);
if(hr == HR_ERROR)
break;
if(m_ullBodyLen > 0)
m_bHeader = FALSE;
else
{
hr = CompleteMessage();
if(hr == HR_ERROR)
break;
}
}
}
}
else
{
iMin = (int)min(m_ullBodyRemain, (ULONGLONG)iRemain);
if(m_lpszMask)
{
int iFactor = (m_ullBodyLen - m_ullBodyRemain) & 0x03;
for(int i = 0; i < iMin; i++)
pTemp[i] = pTemp[i] ^ m_lpszMask[(i + iFactor) & 0x03];
}
m_ullBodyRemain -= iMin;
hr = m_pHttpObj->on_ws_message_body(pTemp, iMin);
if(hr == HR_ERROR)
break;
if(m_ullBodyRemain == 0)
{
hr = CompleteMessage();
if(hr == HR_ERROR)
break;
}
}
pTemp += iMin;
iRemain -= iMin;
}
return hr;
}
BOOL GetMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
{
TBaseWSHeader bh(m_szHeader);
if(lpbFinal) *lpbFinal = bh.fin();
if(lpiReserved) *lpiReserved = bh.rsv();
if(lpiOperationCode) *lpiOperationCode = bh.code();
if(lpszMask) *lpszMask = m_lpszMask;
if(lpullBodyLen) *lpullBodyLen = m_ullBodyLen;
if(lpullBodyRemain) *lpullBodyRemain = m_ullBodyRemain;
return TRUE;
}
BOOL CopyData(const TWSContext& src)
{
if(&src == this)
return FALSE;
memcpy(m_szHeader, src.m_szHeader, HTTP_MAX_WS_HEADER_LEN);
if(src.m_lpszMask)
m_lpszMask = m_szHeader + (src.m_lpszMask - src.m_szHeader);
else
m_lpszMask = nullptr;
m_ullBodyLen = src.m_ullBodyLen;
m_ullBodyRemain = src.m_ullBodyRemain;
return TRUE;
}
public:
TWSContext(T* pHttpObj) : m_pHttpObj(pHttpObj)
{
Reset();
}
private:
EnHandleResult CompleteMessage()
{
EnHandleResult hr = m_pHttpObj->on_ws_message_complete();
Reset();
return hr;
}
void Reset()
{
m_bHeader = TRUE;
m_lpszMask = nullptr;
m_iHeaderLen = HTTP_MIN_WS_HEADER_LEN;
m_iHeaderRemain = HTTP_MIN_WS_HEADER_LEN;
m_ullBodyLen = 0;
m_ullBodyRemain = 0;
}
private:
T* m_pHttpObj;
BYTE m_szHeader[HTTP_MAX_WS_HEADER_LEN];
const BYTE* m_lpszMask;
BOOL m_bHeader;
int m_iHeaderLen;
int m_iHeaderRemain;
ULONGLONG m_ullBodyLen;
ULONGLONG m_ullBodyRemain;
};
// ------------------------------------------------------------------------------------------------------------- //
/* Http 上下文结构 */
template<class T, class S> struct THttpObjT
{
public:
EnHandleResult Execute(const BYTE* pData, int iLength)
{
ASSERT(pData != nullptr && iLength > 0);
if(m_parser.upgrade)
{
if(m_enUpgrade == HUT_WEB_SOCKET)
return m_pwsContext->Parse(pData, iLength);
else
return m_pContext->DoFireSuperReceive(m_pSocket, pData, iLength);
}
EnHandleResult hr = HR_OK;
llhttp_errno rs = ::llhttp_execute(&m_parser, (LPCSTR)pData, iLength);
if(rs == HPE_OK)
ASSERT(m_parser.error_pos == nullptr);
else if(rs == HPE_PAUSED_UPGRADE)
{
int iPased = (m_parser.error_pos == nullptr) ? iLength : (int)((const BYTE*)(m_parser.error_pos) - pData);
hr = Upgrade(pData, iLength, iPased);
}
else
{
m_pContext->FireParseError(m_pSocket, m_parser.error, ::llhttp_get_error_reason(&m_parser));
hr = HR_ERROR;
}
return hr;
}
void CheckBodyIdentityEof()
{
if(!m_parser.upgrade)
::llhttp_finish(&m_parser);
}
static int on_message_begin(http_parser* p)
{
THttpObjT* pSelf = Self(p);
pSelf->ResetHeaderState(FALSE, FALSE);
return pSelf->m_pContext->FireMessageBegin(pSelf->m_pSocket);
}
static int on_url(http_parser* p, const char* at, size_t length)
{
Self(p)->AppendBuffer(at, length);
return HPR_OK;
}
static int on_url_complete(llhttp_t* p)
{
THttpObjT* pSelf = Self(p);
EnHttpParseResult hpr = pSelf->ParseUrl();
if(hpr == HPR_OK)
hpr = pSelf->m_pContext->FireRequestLine(pSelf->m_pSocket, ::llhttp_method_name((llhttp_method_t)p->method), pSelf->GetBuffer());
pSelf->ResetBuffer();
return hpr;
}
static int on_status(http_parser* p, const char* at, size_t length)
{
Self(p)->AppendBuffer(at, length);
return HPR_OK;
}
static int on_status_complete(llhttp_t* p)
{
THttpObjT* pSelf = Self(p);
EnHttpParseResult hpr = pSelf->m_pContext->FireStatusLine(pSelf->m_pSocket, p->status_code, pSelf->GetBuffer());
pSelf->ResetBuffer();
return hpr;
}
static int on_header_field(http_parser* p, const char* at, size_t length)
{
Self(p)->AppendBuffer(at, length);
return HPR_OK;
}
static int on_header_field_complete(llhttp_t* p)
{
THttpObjT* pSelf = Self(p);
pSelf->m_strCurHeader = pSelf->GetBuffer();
pSelf->ResetBuffer();
return HPR_OK;
}
static int on_header_value(http_parser* p, const char* at, size_t length)
{
Self(p)->AppendBuffer(at, length);
return HPR_OK;
}
static int on_header_value_complete(llhttp_t* p)
{
THttpObjT* pSelf = Self(p);
pSelf->m_headers.emplace(move(THeaderMap::value_type(pSelf->m_strCurHeader, pSelf->GetBuffer())));
EnHttpParseResult hpr = pSelf->m_pContext->FireHeader(pSelf->m_pSocket, pSelf->m_strCurHeader, pSelf->GetBuffer());
if(hpr != HPR_ERROR && !pSelf->GetBufferRef().IsEmpty())
{
if(pSelf->m_bRequest && pSelf->m_strCurHeader == HTTP_HEADER_COOKIE)
hpr = pSelf->ParseCookie();
else if(!pSelf->m_bRequest && pSelf->m_strCurHeader == HTTP_HEADER_SET_COOKIE)
hpr = pSelf->ParseSetCookie();
}
pSelf->ResetBuffer();
return hpr;
}
static int on_headers_complete(http_parser* p)
{
THttpObjT* pSelf = Self(p);
pSelf->CheckUpgrade();
pSelf->ResetHeaderBuffer();
EnHttpParseResult rs = pSelf->m_pContext->FireHeadersComplete(pSelf->m_pSocket);
if(!pSelf->m_bRequest && pSelf->GetMethodInt() == HTTP_HEAD && rs == HPR_OK)
rs = HPR_SKIP_BODY;
return rs;
}
static int on_body(http_parser* p, const char* at, size_t length)
{
THttpObjT* pSelf = Self(p);
return pSelf->m_pContext->FireBody(pSelf->m_pSocket, (const BYTE*)at, (int)length);
}
static int on_chunk_header(http_parser* p)
{
THttpObjT* pSelf = Self(p);
return pSelf->m_pContext->FireChunkHeader(pSelf->m_pSocket, (int)p->content_length);
}
static int on_chunk_complete(http_parser* p)
{
THttpObjT* pSelf = Self(p);
return pSelf->m_pContext->FireChunkComplete(pSelf->m_pSocket);
}
static int on_message_complete(http_parser* p)
{
THttpObjT* pSelf = Self(p);
return pSelf->m_pContext->FireMessageComplete(pSelf->m_pSocket);
}
EnHandleResult on_ws_message_header(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
{
return m_pContext->FireWSMessageHeader(m_pSocket, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);
}
EnHandleResult on_ws_message_body(const BYTE* pData, int iLength)
{
return m_pContext->FireWSMessageBody(m_pSocket, pData, iLength);
}
EnHandleResult on_ws_message_complete()
{
return m_pContext->FireWSMessageComplete(m_pSocket);
}
private:
EnHandleResult Upgrade(const BYTE* pData, int iLength, int iPased)
{
ASSERT(m_parser.upgrade);
if(m_pContext->FireUpgrade(m_pSocket, m_enUpgrade) != HPR_OK)
return HR_ERROR;
ResetHeaderState();
if(m_enUpgrade == HUT_WEB_SOCKET)
m_pwsContext = new TWSContext<THttpObjT<T, S>>(this);
if(iPased < iLength)
return Execute(pData + iPased, iLength - iPased);
return HR_OK;
}
void CheckUpgrade()
{
if(!m_parser.upgrade)
return;
if(m_bRequest && m_parser.method == HTTP_CONNECT)
m_enUpgrade = HUT_HTTP_TUNNEL;
else
{
LPCSTR lpszValue;
if(GetHeader(HTTP_HEADER_UPGRADE, &lpszValue) && _stricmp(HTTP_HEADER_VALUE_WEB_SOCKET, lpszValue) == 0)
m_enUpgrade = HUT_WEB_SOCKET;
else
m_enUpgrade = HUT_UNKNOWN;
}
}
EnHttpParseResult ParseUrl()
{
http_parser_url url = {0};
BOOL isConnect = m_parser.method == HTTP_CONNECT;
int rs = ::http_parser_parse_url(m_strBuffer, m_strBuffer.GetLength(), isConnect, &url);
if(rs != HPE_OK)
{
/*
m_parser.error = HPE_INVALID_URL;
m_parser.reason = "Parse url fail";
*/
return HPR_ERROR;
}
m_usUrlFieldSet = url.field_set;
LPCSTR lpszBuffer = m_strBuffer;
for(int i = 0; i < UF_MAX; i++)
{
if((url.field_set & (1 << i)) != 0)
m_pstrUrlFileds[i].SetString((lpszBuffer + url.field_data[i].off), url.field_data[i].len);
}
return HPR_OK;
}
EnHttpParseResult ParseCookie()
{
int i = 0;
do
{
CStringA tk = m_strBuffer.Tokenize(COOKIE_FIELD_SEP, i);
if(i == -1)
break;
int j = tk.Trim().Find(COOKIE_KV_SEP_CHAR);
if(j <= 0)
continue;
/*
{
m_parser.http_errno = HPE_INVALID_HEADER_TOKEN;
return HPR_ERROR;
}
*/
AddCookie(tk.Left(j), tk.Mid(j + 1));
} while(TRUE);
return HPR_OK;
}
EnHttpParseResult ParseSetCookie()
{
CCookieMgr* pCookieMgr = m_pContext->GetCookieMgr();
if(pCookieMgr == nullptr)
return HPR_OK;
LPCSTR lpszDomain = GetDomain();
LPCSTR lpszPath = GetPath();
unique_ptr<CCookie> pCookie(CCookie::FromString(m_strBuffer, lpszDomain, lpszPath));
if(pCookie == nullptr)
return HPR_ERROR;
if(pCookie->Match(lpszDomain, lpszPath, TRUE, m_pContext->IsSecure()))
{
if(pCookie->IsExpired())
DeleteCookie(pCookie->name);
else
AddCookie(pCookie->name, pCookie->value);
}
if(pCookieMgr->IsEnableThirdPartyCookie() || pCookie->IsSameDomain(lpszDomain))
pCookieMgr->SetCookie(*pCookie);
return HPR_OK;
}
public:
int GetCount() const {return 0;}
DWORD GetFreeTime() const {return m_dwFreeTime;}
void SetFree() {m_dwFreeTime = ::TimeGetTime();}
BOOL IsRequest() {return m_bRequest;}
BOOL IsUpgrade() {return m_parser.upgrade;}
BOOL IsKeepAlive() {return ::llhttp_should_keep_alive(&m_parser);}
USHORT GetVersion() {return MAKEWORD(m_parser.http_major, m_parser.http_minor);}
ULONGLONG GetContentLength() {return m_parser.content_length;}
int GetMethodInt() {return m_bRequest ? m_parser.method : m_sRequestMethod;}
USHORT GetUrlFieldSet() {return m_usUrlFieldSet;}
USHORT GetStatusCode() {return m_parser.status_code;}
EnHttpUpgradeType GetUpgradeType() {return m_enUpgrade;}
THeaderMap& GetHeaderMap() {return m_headers;}
TCookieMap& GetCookieMap() {return m_cookies;}
BOOL HasReleased() {return m_bReleased;}
void Release() {m_bReleased = TRUE;}
LPCSTR GetMethod()
{
int iMethod = GetMethodInt();
if(iMethod >= 0 && iMethod <= LLHTTP_MAX_METHOD_NUM)
return ::llhttp_method_name((llhttp_method)iMethod);
return nullptr;
}
LPCSTR GetContentType()
{
LPCSTR lpszValue = nullptr;
GetHeader(HTTP_HEADER_CONTENT_TYPE, &lpszValue);
return lpszValue;
}
LPCSTR GetContentEncoding()
{
LPCSTR lpszValue = nullptr;
GetHeader(HTTP_HEADER_CONTENT_ENCODING, &lpszValue);
return lpszValue;
}
LPCSTR GetTransferEncoding()
{
LPCSTR lpszValue = nullptr;
GetHeader(HTTP_HEADER_TRANSFER_ENCODING, &lpszValue);
return lpszValue;
}
LPCSTR GetHost()
{
LPCSTR lpszValue = nullptr;
GetHeader(HTTP_HEADER_HOST, &lpszValue);
return lpszValue;
}
USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr)
{
USHORT usError = (USHORT)m_parser.error;
if(lpszErrorDesc)
{
if(usError == HPE_OK)
*lpszErrorDesc = ::llhttp_errno_name((llhttp_errno_t)usError);
else
{
*lpszErrorDesc = ::llhttp_get_error_reason(&m_parser);
if(::IsStrEmptyA(*lpszErrorDesc) && usError <= LLHTTP_MAX_ERROR_NUM)
*lpszErrorDesc = ::llhttp_errno_name((llhttp_errno_t)usError);
}
}
return usError;
}
LPCSTR GetUrlField(EnHttpUrlField enField)
{
ASSERT(m_bRequest && enField < HUF_MAX);
if(!m_bRequest || enField >= HUF_MAX)
return nullptr;
return m_pstrUrlFileds[enField];
}
LPCSTR GetPath()
{
if(m_bRequest)
return GetUrlField(HUF_PATH);
else
return *m_pstrRequestPath;
}
LPCSTR GetDomain()
{
ASSERT(!m_bRequest);
return m_pContext->GetRemoteDomain(m_pSocket);
}
LPCSTR GetRequestPath()
{
if(m_bRequest)
return nullptr;
return *m_pstrRequestPath;
}
void SetRequestPath(LPCSTR lpszMethod, LPCSTR lpszPath)
{
ASSERT(!m_bRequest);
if(m_bRequest)
return;
*m_pstrRequestPath = lpszPath;
#define HTTP_METHOD_GEN(NUM, NAME, STRING) else if(_stricmp(lpszMethod, #STRING) == 0) m_sRequestMethod = HTTP_##NAME;
if(::IsStrEmptyA(lpszMethod)) m_sRequestMethod = -1;
HTTP_METHOD_MAP(HTTP_METHOD_GEN)
else
m_sRequestMethod = -1;
#undef HTTP_METHOD_GEN
}
BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue)
{
ASSERT(lpszName);
BOOL isOK = FALSE;
THeaderMapCI it = m_headers.find(lpszName);
if(it != m_headers.end())
{
*lpszValue = it->second;
isOK = TRUE;
}
return isOK;
}
BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
{
ASSERT(lpszName);
if(lpszValue == nullptr || dwCount == 0)
{
dwCount = (DWORD)m_headers.count(lpszName);
return FALSE;
}
pair<THeaderMapCI, THeaderMapCI> range = m_headers.equal_range(lpszName);
THeaderMapCI it = range.first;
DWORD dwIndex = 0;
while(it != range.second)
{
if(dwIndex < dwCount)
lpszValue[dwIndex] = it->second;
++dwIndex;
++it;
}
BOOL isOK = (dwIndex > 0 && dwIndex <= dwCount);
dwCount = dwIndex;
return isOK;
}
BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount)
{
DWORD dwSize = (DWORD)m_headers.size();
if(lpHeaders == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount)
{
dwCount = dwSize;
return FALSE;
}
DWORD dwIndex = 0;
for(THeaderMapCI it = m_headers.begin(), end = m_headers.end(); it != end; ++it, ++dwIndex)
{
lpHeaders[dwIndex].name = it->first;
lpHeaders[dwIndex].value = it->second;
}
dwCount = dwSize;
return TRUE;
}
BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount)
{
DWORD dwSize = (DWORD)m_headers.size();
if(lpszName == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount)
{
dwCount = dwSize;
return FALSE;
}
DWORD dwIndex = 0;
for(THeaderMapCI it = m_headers.begin(), end = m_headers.end(); it != end; ++it, ++dwIndex)
lpszName[dwIndex] = it->first;
dwCount = dwSize;
return TRUE;
}
BOOL AddCookie(LPCSTR lpszName, LPCSTR lpszValue, BOOL bRelpace = TRUE)
{
ASSERT(lpszName);
TCookieMapI it = m_cookies.find(lpszName);
if(it == m_cookies.end())
return m_cookies.emplace(move(TCookieMap::value_type(lpszName, lpszValue))).second;
BOOL isOK = FALSE;
if(bRelpace)
{
it->second = lpszValue;
isOK = TRUE;
}
return isOK;
}
BOOL DeleteCookie(LPCSTR lpszName)
{
ASSERT(lpszName);
return m_cookies.erase(lpszName) > 0;
}
void DeleteAllCookies()
{
m_cookies.clear();
}
BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue)
{
ASSERT(lpszName);
BOOL isOK = FALSE;
TCookieMapCI it = m_cookies.find(lpszName);
if(it != m_cookies.end())
{
*lpszValue = it->second;
isOK = TRUE;
}
return isOK;
}
BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount)
{
DWORD dwSize = (DWORD)m_cookies.size();
if(lpCookies == nullptr || dwCount == 0 || dwSize == 0 || dwSize > dwCount)
{
dwCount = dwSize;
return FALSE;
}
DWORD dwIndex = 0;
for(TCookieMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it, ++dwIndex)
{
lpCookies[dwIndex].name = it->first;
lpCookies[dwIndex].value = it->second;
}
dwCount = dwSize;
return TRUE;
}
BOOL ReloadCookies()
{
CCookieMgr* pCookieMgr = m_pContext->GetCookieMgr();
if(pCookieMgr == nullptr)
return TRUE;
DeleteAllCookies();
CCookieSet cookies;
if(!pCookieMgr->GetCookies(cookies, GetDomain(), GetPath(), TRUE, m_pContext->IsSecure()))
return FALSE;
for(CCookieSetCI it = cookies.begin(), end = cookies.end(); it != end; ++it)
AddCookie(it->name, it->value);
return TRUE;
}
BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
{
if(!m_pwsContext)
return FALSE;
return m_pwsContext->GetMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
}
public:
THttpObjT (BOOL bRequest, T* pContext, S* pSocket)
: m_pContext (pContext)
, m_pSocket (pSocket)
, m_bRequest (bRequest)
, m_bValid (FALSE)
, m_bReleased (FALSE)
, m_dwFreeTime (0)
, m_usUrlFieldSet (m_bRequest ? 0 : -1)
, m_pstrUrlFileds (nullptr)
, m_enUpgrade (HUT_NONE)
, m_pwsContext (nullptr)
{
if(m_bRequest)
m_pstrUrlFileds = new CStringA[HUF_MAX];
else
m_pstrRequestPath = new CStringA;
ResetParser();
}
~THttpObjT()
{
if(m_bRequest)
delete[] m_pstrUrlFileds;
else
delete m_pstrRequestPath;
ReleaseWSContext();
}
static THttpObjT* Construct(BOOL bRequest, T* pContext, S* pSocket)
{return new THttpObjT(bRequest, pContext, pSocket);}
static void Destruct(THttpObjT* pHttpObj)
{if(pHttpObj) delete pHttpObj;}
void Reset(BOOL bValid = FALSE)
{
ResetParser();
ResetHeaderState();
ReleaseWSContext();
m_bValid = bValid;
m_bReleased = FALSE;
m_enUpgrade = HUT_NONE;
m_dwFreeTime = 0;
}
void Renew(T* pContext, S* pSocket)
{
Reset(TRUE);
m_pContext = pContext;
m_pSocket = pSocket;
}
void SetValid(BOOL bValid = TRUE)
{
m_bValid = bValid;
}
BOOL IsValid()
{
return m_bValid;
}
BOOL CopyData(const THttpObjT& src)
{
if(&src == this)
return FALSE;
if(m_bRequest != src.m_bRequest)
return FALSE;
void* p = m_parser.data;
m_parser = src.m_parser;
m_parser.data = p;
m_headers = src.m_headers;
m_cookies = src.m_cookies;
if(m_bRequest)
{
m_usUrlFieldSet = src.m_usUrlFieldSet;
for(int i = 0;i < HUF_MAX; i++)
m_pstrUrlFileds[i] = src.m_pstrUrlFileds[i];
}
else
{
m_sRequestMethod = src.m_sRequestMethod;
*m_pstrRequestPath = *src.m_pstrRequestPath;
}
m_enUpgrade = src.m_enUpgrade;
return TRUE;
}
BOOL CopyWSContext(const THttpObjT& src)
{
if(&src == this)
return FALSE;
if(m_bRequest != src.m_bRequest)
return FALSE;
if(!src.m_pwsContext && !m_pwsContext)
;
else if(!src.m_pwsContext && m_pwsContext)
{
delete m_pwsContext;
m_pwsContext = nullptr;
}
else
{
if(!m_pwsContext)
m_pwsContext = new TWSContext<THttpObjT<T, S>>(this);
m_pwsContext->CopyData(*src.m_pwsContext);
}
return TRUE;
}
private:
void ResetParser()
{
::llhttp_init(&m_parser, m_bRequest ? HTTP_REQUEST : HTTP_RESPONSE, &sm_settings);
m_parser.data = this;
}
void ResetHeaderState(BOOL bClearCookies = TRUE, BOOL bResetRequestData = TRUE)
{
if(m_bRequest)
{
if(m_usUrlFieldSet != 0)
{
m_usUrlFieldSet = 0;
for(int i = 0; i < HUF_MAX; i++)
m_pstrUrlFileds[i].Empty();
}
}
else
{
if(bResetRequestData)
{
m_sRequestMethod = -1;
m_pstrRequestPath->Empty();
}
}
if(m_bRequest || bClearCookies)
DeleteAllCookies();
m_headers.clear();
ResetHeaderBuffer();
}
void ResetHeaderBuffer()
{
ResetBuffer();
m_strCurHeader.Empty();
}
void ReleaseWSContext()
{
if(m_pwsContext)
{
delete m_pwsContext;
m_pwsContext = nullptr;
}
}
void AppendBuffer(const char* at, size_t length) {m_strBuffer.Append(at, (int)length);}
void ResetBuffer() {m_strBuffer.Empty();}
LPCSTR GetBuffer() {return m_strBuffer;}
CStringA& GetBufferRef() {return m_strBuffer;}
static THttpObjT* Self(http_parser* p) {return (THttpObjT*)(p->data);}
static T* SelfContext(http_parser* p) {return Self(p)->m_pContext;}
static S* SelfSocketObj(http_parser* p) {return Self(p)->m_pSocket;}
private:
BOOL m_bValid;
BOOL m_bRequest;
BOOL m_bReleased;
T* m_pContext;
S* m_pSocket;
http_parser m_parser;
THeaderMap m_headers;
TCookieMap m_cookies;
CStringA m_strBuffer;
CStringA m_strCurHeader;
union
{
USHORT m_usUrlFieldSet;
short m_sRequestMethod;
};
union
{
CStringA* m_pstrUrlFileds;
CStringA* m_pstrRequestPath;
};
EnHttpUpgradeType m_enUpgrade;
DWORD m_dwFreeTime;
TWSContext<THttpObjT<T, S>>* m_pwsContext;
static http_parser_settings sm_settings;
};
template<class T, class S> http_parser_settings THttpObjT<T, S>::sm_settings =
{
on_message_begin,
on_url,
on_status,
on_header_field,
on_header_value,
on_headers_complete,
on_body,
on_message_complete,
on_chunk_header,
on_chunk_complete,
on_url_complete,
on_status_complete,
on_header_field_complete,
on_header_value_complete
};
// ------------------------------------------------------------------------------------------------------------- //
template<BOOL is_request, class T, class S> class CHttpObjPoolT
{
typedef THttpObjT<T, S> THttpObj;
typedef CRingPool<THttpObj> TSSLHttpObjList;
typedef CCASQueue<THttpObj> TSSLHttpObjQueue;
public:
THttpObj* PickFreeHttpObj(T* pContext, S* pSocket)
{
DWORD dwIndex;
THttpObj* pHttpObj = nullptr;
if(m_lsFreeHttpObj.TryLock(&pHttpObj, dwIndex))
{
if(::GetTimeGap32(pHttpObj->GetFreeTime()) >= m_dwHttpObjLockTime)
ENSURE(m_lsFreeHttpObj.ReleaseLock(nullptr, dwIndex));
else
{
ENSURE(m_lsFreeHttpObj.ReleaseLock(pHttpObj, dwIndex));
pHttpObj = nullptr;
}
}
if(pHttpObj)
pHttpObj->Renew(pContext, pSocket);
else
{
pHttpObj = THttpObj::Construct(is_request, pContext, pSocket);
ASSERT(pHttpObj);
}
return pHttpObj;
}
void PutFreeHttpObj(THttpObj* pHttpObj)
{
pHttpObj->SetFree();
ReleaseGCHttpObj();
if(!m_lsFreeHttpObj.TryPut(pHttpObj))
m_lsGCHttpObj.PushBack(pHttpObj);
}
void Prepare()
{
m_lsFreeHttpObj.Reset(m_dwHttpObjPoolSize);
}
void Clear()
{
m_lsFreeHttpObj.Clear();
ReleaseGCHttpObj(TRUE);
ENSURE(m_lsGCHttpObj.IsEmpty());
}
private:
void ReleaseGCHttpObj(BOOL bForce = FALSE)
{
::ReleaseGCObj(m_lsGCHttpObj, m_dwHttpObjLockTime, bForce);
}
public:
void SetHttpObjLockTime (DWORD dwHttpObjLockTime) {m_dwHttpObjLockTime = dwHttpObjLockTime;}
void SetHttpObjPoolSize (DWORD dwHttpObjPoolSize) {m_dwHttpObjPoolSize = dwHttpObjPoolSize;}
void SetHttpObjPoolHold (DWORD dwHttpObjPoolHold) {m_dwHttpObjPoolHold = dwHttpObjPoolHold;}
DWORD GetHttpObjLockTime() {return m_dwHttpObjLockTime;}
DWORD GetHttpObjPoolSize() {return m_dwHttpObjPoolSize;}
DWORD GetHttpObjPoolHold() {return m_dwHttpObjPoolHold;}
public:
CHttpObjPoolT( DWORD dwPoolSize = DEFAULT_HTTPOBJ_POOL_SIZE,
DWORD dwPoolHold = DEFAULT_HTTPOBJ_POOL_HOLD,
DWORD dwLockTime = DEFAULT_HTTPOBJ_LOCK_TIME)
: m_dwHttpObjPoolSize(dwPoolSize)
, m_dwHttpObjPoolHold(dwPoolHold)
, m_dwHttpObjLockTime(dwLockTime)
{
}
~CHttpObjPoolT() {Clear();}
DECLARE_NO_COPY_CLASS(CHttpObjPoolT)
public:
static const DWORD DEFAULT_HTTPOBJ_LOCK_TIME;
static const DWORD DEFAULT_HTTPOBJ_POOL_SIZE;
static const DWORD DEFAULT_HTTPOBJ_POOL_HOLD;
private:
DWORD m_dwHttpObjLockTime;
DWORD m_dwHttpObjPoolSize;
DWORD m_dwHttpObjPoolHold;
TSSLHttpObjList m_lsFreeHttpObj;
TSSLHttpObjQueue m_lsGCHttpObj;
};
template<BOOL is_request, class T, class S> const DWORD CHttpObjPoolT<is_request, T, S>::DEFAULT_HTTPOBJ_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME;
template<BOOL is_request, class T, class S> const DWORD CHttpObjPoolT<is_request, T, S>::DEFAULT_HTTPOBJ_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE;
template<BOOL is_request, class T, class S> const DWORD CHttpObjPoolT<is_request, T, S>::DEFAULT_HTTPOBJ_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD;
// ------------------------------------------------------------------------------------------------------------- //
extern CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult);
extern CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath);
extern LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode);
extern void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue);
extern void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue);
extern void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue);
extern void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2]);
extern int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5]);
extern BOOL MakeWSPacket(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], BYTE* pData, int iLength, ULONGLONG ullBodyLen, BYTE szHeader[HTTP_MAX_WS_HEADER_LEN], WSABUF szBuffer[2]);
extern BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath);
#endif