mirror of
https://gitee.com/ldcsaa/HP-Socket.git
synced 2024-12-05 13:17:53 +08:00
1389 lines
31 KiB
C++
1389 lines
31 KiB
C++
/*
|
||
* 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 (5 * 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)
|
||
{
|
||
if(m_pwsContext != nullptr)
|
||
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.CompareNoCase(HTTP_HEADER_COOKIE) == 0)
|
||
hpr = pSelf->ParseCookie();
|
||
else if(!pSelf->m_bRequest && pSelf->m_strCurHeader.CompareNoCase(HTTP_HEADER_SET_COOKIE) == 0)
|
||
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,
|
||
nullptr, // on_method
|
||
nullptr, // on_version
|
||
on_header_field,
|
||
on_header_value,
|
||
nullptr, // on_chunk_extension_name
|
||
nullptr, // on_chunk_extension_value
|
||
on_headers_complete,
|
||
on_body,
|
||
on_message_complete,
|
||
on_url_complete,
|
||
on_status_complete,
|
||
nullptr, // on_method_complete
|
||
nullptr, // on_version_complete
|
||
on_header_field_complete,
|
||
on_header_value_complete,
|
||
nullptr, // on_chunk_extension_name_complete
|
||
nullptr, // on_chunk_extension_value_complete
|
||
on_chunk_header,
|
||
on_chunk_complete,
|
||
nullptr // on_reset
|
||
};
|
||
|
||
// ------------------------------------------------------------------------------------------------------------- //
|
||
|
||
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();
|
||
|
||
#ifndef USE_EXTERNAL_GC
|
||
ReleaseGCHttpObj();
|
||
#endif
|
||
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());
|
||
}
|
||
|
||
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 |