/* * 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 "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_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", (LPCSTR)(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 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 = (LPBYTE)(LPCSTR)strHeader; szBuffer[0].len = strHeader.GetLength(); szBuffer[1].buf = (LPBYTE)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 = (LPBYTE)szLen; bufs[i].len = (int)strlen(szLen); ++i; } else { LPCSTR lpszSep = lpszExtensions[0] == ';' ? " " : " ;"; sprintf(szLen, "%x%s", iLength, lpszSep); bufs[i].buf = (LPBYTE)szLen; bufs[i].len = (int)strlen(szLen); ++i; bufs[i].buf = (LPBYTE)lpszExtensions; bufs[i].len = (int)strlen(lpszExtensions); ++i; bufs[i].buf = (LPBYTE)HTTP_CRLF; bufs[i].len = 2; ++i; } if(iLength > 0) { bufs[i].buf = (LPBYTE)pData; bufs[i].len = iLength; ++i; } bufs[i].buf = (LPBYTE)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 = szHeader; szBuffer[0].len = iHeaderLen; szBuffer[1].buf = (LPBYTE)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