| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489 |
- /*
- * Copyright: JessMA Open Source (ldcsaa@gmail.com)
- *
- * Author : Bruce Liang
- * Website : http://www.jessma.org
- * Project : https://github.com/ldcsaa
- * Blog : http://www.cnblogs.com/ldcsaa
- * Wiki : http://www.oschina.net/p/hp-socket
- * QQ Group : 75375912, 44636872
- *
- * 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
- ///////////////////////////////////////////////////////////////////////////////////////////////////////
- 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_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_IM_USED : return "IM Used";
- 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_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: return "Request Header Fields Too Large";
- case HSC_UNAVAILABLE_FOR_LEGAL_REASONS : return "Unavailable For Legal Reasons";
- case HSC_RETRY_WITH : return "Retry With";
- 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_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_hash_func::hash, str_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;
- }
- 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;
- }
- int Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
- {
- return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
- }
- int CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy)
- {
- z_stream stream;
- stream.next_in = (z_const Bytef*)lpszSrc;
- stream.avail_in = dwSrcLen;
- stream.next_out = lpszDest;
- stream.avail_out = dwDestLen;
- stream.zalloc = nullptr;
- stream.zfree = nullptr;
- stream.opaque = nullptr;
- int err = ::deflateInit2(&stream, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy);
- if(err != Z_OK) return err;
- err = ::deflate(&stream, Z_FINISH);
- if(err != Z_STREAM_END)
- {
- ::deflateEnd(&stream);
- return err == Z_OK ? Z_BUF_ERROR : err;
- }
- if(dwDestLen > stream.total_out)
- {
- lpszDest[stream.total_out] = 0;
- dwDestLen = stream.total_out;
- }
- return ::deflateEnd(&stream);
- }
- int Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
- {
- return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
- }
- int UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits)
- {
- z_stream stream;
- stream.next_in = (z_const Bytef*)lpszSrc;
- stream.avail_in = (uInt)dwSrcLen;
- stream.next_out = lpszDest;
- stream.avail_out = dwDestLen;
- stream.zalloc = nullptr;
- stream.zfree = nullptr;
- int err = ::inflateInit2(&stream, iWindowBits);
- if(err != Z_OK) return err;
- err = ::inflate(&stream, Z_FINISH);
- if(err != Z_STREAM_END)
- {
- ::inflateEnd(&stream);
- return (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) ? Z_DATA_ERROR : err;
- }
- if(dwDestLen > stream.total_out)
- {
- lpszDest[stream.total_out] = 0;
- dwDestLen = stream.total_out;
- }
- return inflateEnd(&stream);
- }
- DWORD GuessCompressBound(DWORD dwSrcLen, BOOL bGZip)
- {
- DWORD dwBound = ::compressBound(dwSrcLen);
-
- if(bGZip) dwBound += 11;
- return dwBound;
- }
- int GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
- {
- return CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 16);
- }
- int GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
- {
- return UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, MAX_WBITS + 32);
- }
- DWORD GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen)
- {
- if(dwSrcLen < 20 || *(USHORT*)lpszSrc != 0x8B1F)
- return 0;
- return *(DWORD*)(lpszSrc + dwSrcLen - 4);
- }
- #endif
|