HP-Socket/Windows/Src/HttpHelper.cpp
2023-07-02 01:23:52 +08:00

472 lines
15 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.
*/
#include "stdafx.h"
#include "HttpHelper.h"
#ifdef _HTTP_SUPPORT
#pragma warning(disable: 4840)
///////////////////////////////////////////////////////////////////////////////////////////////////////
CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult)
{
strResult.Format("HTTP/%d.%d", LOBYTE(enVersion), HIBYTE(enVersion));
return strResult;
}
CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath)
{
strPath = lpszPath;
if(strPath.IsEmpty() || (!bConnect && strPath.GetAt(0) != HTTP_PATH_SEPARATOR_CHAR))
strPath.Insert(0, HTTP_PATH_SEPARATOR_CHAR);
return strPath;
}
LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode)
{
switch(enCode)
{
case HSC_CONTINUE : return "Continue";
case HSC_SWITCHING_PROTOCOLS : return "Switching Protocols";
case HSC_PROCESSING : return "Processing";
case HSC_EARLY_HINTS : return "Early Hints";
case HSC_RESPONSE_IS_STALE : return "Response Is Stale";
case HSC_REVALIDATION_FAILED : return "Revalidation Failed";
case HSC_DISCONNECTED_OPERATION : return "Disconnected Operation";
case HSC_HEURISTIC_EXPIRATION : return "Heuristic Expiration";
case HSC_MISCELLANEOUS_WARNING : return "Miscellaneous Warning";
case HSC_OK : return "OK";
case HSC_CREATED : return "Created";
case HSC_ACCEPTED : return "Accepted";
case HSC_NON_AUTHORITATIVE_INFORMATION : return "Non-Authoritative Information";
case HSC_NO_CONTENT : return "No Content";
case HSC_RESET_CONTENT : return "Reset Content";
case HSC_PARTIAL_CONTENT : return "Partial Content";
case HSC_MULTI_STATUS : return "Multi-Status";
case HSC_ALREADY_REPORTED : return "Already Reported";
case HSC_TRANSFORMATION_APPLIED : return "Transformation Applied";
case HSC_IM_USED : return "IM Used";
case HSC_MISCELLANEOUS_PERSISTENT_WARNING : return "Miscellaneous Persistent Warning";
case HSC_MULTIPLE_CHOICES : return "Multiple Choices";
case HSC_MOVED_PERMANENTLY : return "Moved Permanently";
case HSC_MOVED_TEMPORARILY : return "Move temporarily";
case HSC_SEE_OTHER : return "See Other";
case HSC_NOT_MODIFIED : return "Not Modified";
case HSC_USE_PROXY : return "Use Proxy";
case HSC_SWITCH_PROXY : return "Switch Proxy";
case HSC_TEMPORARY_REDIRECT : return "Temporary Redirect";
case HSC_PERMANENT_REDIRECT : return "Permanent Redirect";
case HSC_BAD_REQUEST : return "Bad Request";
case HSC_UNAUTHORIZED : return "Unauthorized";
case HSC_PAYMENT_REQUIRED : return "Payment Required";
case HSC_FORBIDDEN : return "Forbidden";
case HSC_NOT_FOUND : return "Not Found";
case HSC_METHOD_NOT_ALLOWED : return "Method Not Allowed";
case HSC_NOT_ACCEPTABLE : return "Not Acceptable";
case HSC_PROXY_AUTHENTICATION_REQUIRED : return "Proxy Authentication Required";
case HSC_REQUEST_TIMEOUT : return "Request Timeout";
case HSC_CONFLICT : return "Conflict";
case HSC_GONE : return "Gone";
case HSC_LENGTH_REQUIRED : return "Length Required";
case HSC_PRECONDITION_FAILED : return "Precondition Failed";
case HSC_REQUEST_ENTITY_TOO_LARGE : return "Request Entity Too Large";
case HSC_REQUEST_URI_TOO_LONG : return "Request-URI Too Long";
case HSC_UNSUPPORTED_MEDIA_TYPE : return "Unsupported Media Type";
case HSC_REQUESTED_RANGE_NOT_SATISFIABLE : return "Requested Range Not Satisfiable";
case HSC_EXPECTATION_FAILED : return "Expectation Failed";
case HSC_IM_A_TEAPOT : return "I'm a teapot";
case HSC_PAGE_EXPIRED : return "Page Expired";
case HSC_ENHANCE_YOUR_CALM : return "Enhance Your Calm";
case HSC_MISDIRECTED_REQUEST : return "Misdirected Request";
case HSC_UNPROCESSABLE_ENTITY : return "Unprocessable Entity";
case HSC_LOCKED : return "Locked";
case HSC_FAILED_DEPENDENCY : return "Failed Dependency";
case HSC_UNORDERED_COLLECTION : return "Unordered Collection";
case HSC_UPGRADE_REQUIRED : return "Upgrade Required";
case HSC_PRECONDITION_REQUIRED : return "Precondition Required";
case HSC_TOO_MANY_REQUESTS : return "Too Many Requests";
case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL : return "Request Header Fields Too Large Unofficial";
case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE : return "Request Header Fields Too Large";
case HSC_LOGIN_TIMEOUT : return "Login Timeout";
case HSC_NO_RESPONSE : return "No Response";
case HSC_RETRY_WITH : return "Retry With";
case HSC_BLOCKED_BY_PARENTAL_CONTROL : return "Blocked By Parental Control";
case HSC_UNAVAILABLE_FOR_LEGAL_REASONS : return "Unavailable For Legal Reasons";
case HSC_CLIENT_CLOSED_LOAD_BALANCED_REQUEST : return "Client Closed Load Balance Request";
case HSC_INVALID_X_FORWARDED_FOR : return "Invalid X-Forwarded-For";
case HSC_REQUEST_HEADER_TOO_LARGE : return "Request Header Too Large";
case HSC_SSL_CERTIFICATE_ERROR : return "SSL Certificate Error";
case HSC_SSL_CERTIFICATE_REQUIRED : return "SSL Certificate Required";
case HSC_HTTP_REQUEST_SENT_TO_HTTPS_PORT : return "HTTP Request Sent To HTTPS Port";
case HSC_INVALID_TOKEN : return "Invalid Token";
case HSC_CLIENT_CLOSED_REQUEST : return "Client Closed Request";
case HSC_INTERNAL_SERVER_ERROR : return "Internal Server Error";
case HSC_NOT_IMPLEMENTED : return "Not Implemented";
case HSC_BAD_GATEWAY : return "Bad Gateway";
case HSC_SERVICE_UNAVAILABLE : return "Service Unavailable";
case HSC_GATEWAY_TIMEOUT : return "Gateway Timeout";
case HSC_HTTP_VERSION_NOT_SUPPORTED : return "HTTP Version Not Supported";
case HSC_VARIANT_ALSO_NEGOTIATES : return "Variant Also Negotiates";
case HSC_INSUFFICIENT_STORAGE : return "Insufficient Storage";
case HSC_LOOP_DETECTED : return "Loop Detected";
case HSC_BANDWIDTH_LIMIT_EXCEEDED : return "Bandwidth Limit Exceeded";
case HSC_NOT_EXTENDED : return "Not Extended";
case HSC_NETWORK_AUTHENTICATION_REQUIRED : return "Network Authentication Required";
case HSC_WEB_SERVER_UNKNOWN_ERROR : return "Web Server Unknown Error";
case HSC_WEB_SERVER_IS_DOWN : return "Web Server Is Down";
case HSC_CONNECTION_TIMEOUT : return "Connection Timeout";
case HSC_ORIGIN_IS_UNREACHABLE : return "Origin Is Unreachable";
case HSC_TIMEOUT_OCCURED : return "Timeout Occured";
case HSC_SSL_HANDSHAKE_FAILED : return "SSL Handshake Failed";
case HSC_INVALID_SSL_CERTIFICATE : return "Invalid SSL Certificate";
case HSC_RAILGUN_ERROR : return "Railgun Error";
case HSC_SITE_IS_OVERLOADED : return "Site Is Overloaded";
case HSC_SITE_IS_FROZEN : return "Site Is Frozen";
case HSC_IDENTITY_PROVIDER_AUTHENTICATION_ERROR : return "Identity Provider Authentication Error";
case HSC_NETWORK_READ_TIMEOUT : return "Network Read Timeout";
case HSC_NETWORK_CONNECT_TIMEOUT : return "Network Connect Timeout";
case HSC_UNPARSEABLE_RESPONSE_HEADERS : return "Unparseable Response Headers";
default : return "***";
}
}
static inline CStringA& AppendHeader(LPCSTR lpszName, LPCSTR lpszValue, CStringA& strValue)
{
strValue.Append(lpszName);
strValue.Append(HTTP_HEADER_SEPARATOR);
strValue.Append(lpszValue);
strValue.Append(HTTP_CRLF);
return strValue;
}
void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue)
{
ASSERT(lpszMethod);
strValue.Format("%s %s HTTP/%d.%d%s", CStringA(lpszMethod).MakeUpper(), lpszPath, LOBYTE(enVersion), HIBYTE(enVersion), HTTP_CRLF);
}
void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue)
{
if(!lpszDesc) lpszDesc = ::GetHttpDefaultStatusCodeDesc((EnHttpStatusCode)usStatusCode);
strValue.Format("HTTP/%d.%d %d %s%s", LOBYTE(enVersion), HIBYTE(enVersion), usStatusCode, lpszDesc, HTTP_CRLF);
}
void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue)
{
unordered_set<LPCSTR, str_nc_hash_func::hash, str_nc_hash_func::equal_to> szHeaderNames;
if(iHeaderCount > 0)
{
ASSERT(lpHeaders);
for(int i = 0; i < iHeaderCount; i++)
{
const THeader& header = lpHeaders[i];
ASSERT(!::IsStrEmptyA(header.name));
if(!::IsStrEmptyA(header.name))
{
szHeaderNames.emplace(header.name);
AppendHeader(header.name, header.value, strValue);
}
}
}
if( (!bRequest || iBodyLength > 0) &&
(szHeaderNames.empty() ||
(szHeaderNames.find(HTTP_HEADER_CONTENT_LENGTH) == szHeaderNames.end() &&
szHeaderNames.find(HTTP_HEADER_TRANSFER_ENCODING) == szHeaderNames.end())))
{
char szBodyLength[16];
_itoa(iBodyLength, szBodyLength, 10);
AppendHeader(HTTP_HEADER_CONTENT_LENGTH, szBodyLength, strValue);
}
if( (iConnFlag == 0 || iConnFlag == 1) &&
(szHeaderNames.empty() ||
szHeaderNames.find(HTTP_HEADER_CONNECTION) == szHeaderNames.end() ))
{
LPCSTR lpszValue = iConnFlag == 0 ? HTTP_CONNECTION_CLOSE_VALUE : HTTP_CONNECTION_KEEPALIVE_VALUE;
AppendHeader(HTTP_HEADER_CONNECTION, lpszValue, strValue);
}
if( bRequest && !::IsStrEmptyA(lpszDefaultHost) &&
(szHeaderNames.empty() ||
(szHeaderNames.find(HTTP_HEADER_HOST) == szHeaderNames.end()) ))
{
CStringA strHost(lpszDefaultHost);
if(usPort != 0) strHost.AppendFormat(":%u", usPort);
AppendHeader(HTTP_HEADER_HOST, strHost, strValue);
}
szHeaderNames.clear();
if(pCookies != nullptr)
{
DWORD dwSize = (DWORD)pCookies->size();
if(dwSize > 0)
{
strValue.Append(HTTP_HEADER_COOKIE);
strValue.Append(HTTP_HEADER_SEPARATOR);
DWORD dwIndex = 0;
for(TCookieMapCI it = pCookies->begin(), end = pCookies->end(); it != end; ++it, ++dwIndex)
{
strValue.Append(it->first);
strValue.AppendChar(COOKIE_KV_SEP_CHAR);
strValue.Append(it->second);
if(dwIndex < dwSize - 1)
strValue.Append(HTTP_COOKIE_SEPARATOR);
}
strValue.Append(HTTP_CRLF);
}
}
strValue.Append(HTTP_CRLF);
}
void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2])
{
ASSERT(pBody != nullptr || iLength == 0);
szBuffer[0].buf = (LPSTR)(LPCSTR)strHeader;
szBuffer[0].len = strHeader.GetLength();
szBuffer[1].buf = (LPSTR)(LPCSTR)pBody;
szBuffer[1].len = iLength;
}
int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5])
{
ASSERT(iLength == 0 || pData != nullptr);
int i = 0;
if(::IsStrEmptyA(lpszExtensions))
{
sprintf(szLen, "%x" HTTP_CRLF, iLength);
bufs[i].buf = szLen;
bufs[i].len = (int)strlen(szLen);
++i;
}
else
{
LPCSTR lpszSep = lpszExtensions[0] == ';' ? " " : " ;";
sprintf(szLen, "%x%s", iLength, lpszSep);
bufs[i].buf = szLen;
bufs[i].len = (int)strlen(szLen);
++i;
bufs[i].buf = (LPSTR)lpszExtensions;
bufs[i].len = (int)strlen(lpszExtensions);
++i;
bufs[i].buf = HTTP_CRLF;
bufs[i].len = 2;
++i;
}
if(iLength > 0)
{
bufs[i].buf = (LPSTR)(LPCSTR)pData;
bufs[i].len = iLength;
++i;
}
bufs[i].buf = HTTP_CRLF;
bufs[i].len = 2;
++i;
return i;
}
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])
{
ULONGLONG ullLength = (ULONGLONG)iLength;
ASSERT(pData != nullptr || iLength == 0);
ASSERT(ullBodyLen == 0 || ullBodyLen >= ullLength);
if(ullBodyLen == 0)
ullBodyLen = ullLength;
else if(ullBodyLen < ullLength)
{
::SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
TBaseWSHeader bh(szHeader, TRUE);
int iHeaderLen = HTTP_MIN_WS_HEADER_LEN;
bh.set_fin(bFinal);
bh.set_rsv(iReserved);
bh.set_code(iOperationCode);
bh.set_mask(lpszMask ? TRUE : FALSE);
if(ullBodyLen < 126)
bh.set_len((BYTE)ullBodyLen);
else if(ullBodyLen <= 0xFFFF)
{
bh.set_len(126);
bh.set_extlen((USHORT)ullBodyLen);
iHeaderLen += 2;
}
else
{
bh.set_len(127);
*(ULONGLONG*)(szHeader + HTTP_MIN_WS_HEADER_LEN) = ::HToN64(ullBodyLen);
iHeaderLen += 8;
}
if(lpszMask)
{
memcpy(szHeader + iHeaderLen, lpszMask, 4);
for(int i = 0; i < iLength; i++)
pData[i] = pData[i] ^ lpszMask[i & 0x03];
iHeaderLen += 4;
}
szBuffer[0].buf = (LPSTR)(LPCSTR)szHeader;
szBuffer[0].len = iHeaderLen;
szBuffer[1].buf = (LPSTR)(LPCSTR)pData;
szBuffer[1].len = iLength;
return TRUE;
}
BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath)
{
int iSchemaLength = (int)strlen(HTTP_SCHEMA);
if(_strnicmp(strUrl, HTTP_SCHEMA, iSchemaLength) == 0)
bHttps = FALSE;
else
{
iSchemaLength = (int)strlen(HTTPS_SCHEMA);
if(_strnicmp(strUrl, HTTPS_SCHEMA, iSchemaLength) == 0)
bHttps = TRUE;
else
return FALSE;
}
CStringA strFullHost;
int i = strUrl.Find(HTTP_PATH_SEPARATOR_CHAR, iSchemaLength);
if(i > 0)
{
strFullHost = strUrl.Mid(iSchemaLength, i - iSchemaLength);
strPath = strUrl.Mid(i);
}
else
{
strFullHost = strUrl.Mid(iSchemaLength);
strPath = HTTP_PATH_SEPARATOR;
}
if(strFullHost.IsEmpty())
return FALSE;
CStringA strPort;
char c = strFullHost.GetAt(0);
if(!::isalnum(c))
{
if(c != IPV6_ADDR_BEGIN_CHAR)
return FALSE;
else
{
i = strFullHost.ReverseFind(IPV6_ADDR_END_CHAR);
if(i < 0)
return FALSE;
else
{
if(strFullHost.GetLength() > i + 1)
{
if(strFullHost.GetAt(i + 1) != PORT_SEPARATOR_CHAR)
return FALSE;
strPort = strFullHost.Mid(i + 2);
if(strPort.IsEmpty())
return FALSE;
}
strHost = strFullHost.Mid(1, i - 1);
}
}
}
else
{
i = strFullHost.Find(PORT_SEPARATOR_CHAR);
if(i < 0)
strHost = strFullHost;
else
{
strPort = strFullHost.Mid(i + 1);
if(strPort.IsEmpty())
return FALSE;
strHost = strFullHost.Mid(0, i);
}
}
if(strPort.IsEmpty())
usPort = bHttps ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT;
else
usPort = (USHORT)::atoi(strPort);
return TRUE;
}
#endif