HttpHelper.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. /*
  2. * Copyright: JessMA Open Source (ldcsaa@gmail.com)
  3. *
  4. * Author : Bruce Liang
  5. * Website : https://github.com/ldcsaa
  6. * Project : https://github.com/ldcsaa/HP-Socket
  7. * Blog : http://www.cnblogs.com/ldcsaa
  8. * Wiki : http://www.oschina.net/p/hp-socket
  9. * QQ Group : 44636872, 75375912
  10. *
  11. * Licensed under the Apache License, Version 2.0 (the "License");
  12. * you may not use this file except in compliance with the License.
  13. * You may obtain a copy of the License at
  14. *
  15. * http://www.apache.org/licenses/LICENSE-2.0
  16. *
  17. * Unless required by applicable law or agreed to in writing, software
  18. * distributed under the License is distributed on an "AS IS" BASIS,
  19. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  20. * See the License for the specific language governing permissions and
  21. * limitations under the License.
  22. */
  23. #include "HttpHelper.h"
  24. #ifdef _HTTP_SUPPORT
  25. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  26. CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult)
  27. {
  28. strResult.Format("HTTP/%d.%d", LOBYTE(enVersion), HIBYTE(enVersion));
  29. return strResult;
  30. }
  31. CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath)
  32. {
  33. strPath = lpszPath;
  34. if(strPath.IsEmpty() || (!bConnect && strPath.GetAt(0) != HTTP_PATH_SEPARATOR_CHAR))
  35. strPath.Insert(0, HTTP_PATH_SEPARATOR_CHAR);
  36. return strPath;
  37. }
  38. LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode)
  39. {
  40. switch(enCode)
  41. {
  42. case HSC_CONTINUE : return "Continue";
  43. case HSC_SWITCHING_PROTOCOLS : return "Switching Protocols";
  44. case HSC_PROCESSING : return "Processing";
  45. case HSC_EARLY_HINTS : return "Early Hints";
  46. case HSC_RESPONSE_IS_STALE : return "Response Is Stale";
  47. case HSC_REVALIDATION_FAILED : return "Revalidation Failed";
  48. case HSC_DISCONNECTED_OPERATION : return "Disconnected Operation";
  49. case HSC_HEURISTIC_EXPIRATION : return "Heuristic Expiration";
  50. case HSC_MISCELLANEOUS_WARNING : return "Miscellaneous Warning";
  51. case HSC_OK : return "OK";
  52. case HSC_CREATED : return "Created";
  53. case HSC_ACCEPTED : return "Accepted";
  54. case HSC_NON_AUTHORITATIVE_INFORMATION : return "Non-Authoritative Information";
  55. case HSC_NO_CONTENT : return "No Content";
  56. case HSC_RESET_CONTENT : return "Reset Content";
  57. case HSC_PARTIAL_CONTENT : return "Partial Content";
  58. case HSC_MULTI_STATUS : return "Multi-Status";
  59. case HSC_ALREADY_REPORTED : return "Already Reported";
  60. case HSC_TRANSFORMATION_APPLIED : return "Transformation Applied";
  61. case HSC_IM_USED : return "IM Used";
  62. case HSC_MISCELLANEOUS_PERSISTENT_WARNING : return "Miscellaneous Persistent Warning";
  63. case HSC_MULTIPLE_CHOICES : return "Multiple Choices";
  64. case HSC_MOVED_PERMANENTLY : return "Moved Permanently";
  65. case HSC_MOVED_TEMPORARILY : return "Move temporarily";
  66. case HSC_SEE_OTHER : return "See Other";
  67. case HSC_NOT_MODIFIED : return "Not Modified";
  68. case HSC_USE_PROXY : return "Use Proxy";
  69. case HSC_SWITCH_PROXY : return "Switch Proxy";
  70. case HSC_TEMPORARY_REDIRECT : return "Temporary Redirect";
  71. case HSC_PERMANENT_REDIRECT : return "Permanent Redirect";
  72. case HSC_BAD_REQUEST : return "Bad Request";
  73. case HSC_UNAUTHORIZED : return "Unauthorized";
  74. case HSC_PAYMENT_REQUIRED : return "Payment Required";
  75. case HSC_FORBIDDEN : return "Forbidden";
  76. case HSC_NOT_FOUND : return "Not Found";
  77. case HSC_METHOD_NOT_ALLOWED : return "Method Not Allowed";
  78. case HSC_NOT_ACCEPTABLE : return "Not Acceptable";
  79. case HSC_PROXY_AUTHENTICATION_REQUIRED : return "Proxy Authentication Required";
  80. case HSC_REQUEST_TIMEOUT : return "Request Timeout";
  81. case HSC_CONFLICT : return "Conflict";
  82. case HSC_GONE : return "Gone";
  83. case HSC_LENGTH_REQUIRED : return "Length Required";
  84. case HSC_PRECONDITION_FAILED : return "Precondition Failed";
  85. case HSC_REQUEST_ENTITY_TOO_LARGE : return "Request Entity Too Large";
  86. case HSC_REQUEST_URI_TOO_LONG : return "Request-URI Too Long";
  87. case HSC_UNSUPPORTED_MEDIA_TYPE : return "Unsupported Media Type";
  88. case HSC_REQUESTED_RANGE_NOT_SATISFIABLE : return "Requested Range Not Satisfiable";
  89. case HSC_EXPECTATION_FAILED : return "Expectation Failed";
  90. case HSC_IM_A_TEAPOT : return "I'm a teapot";
  91. case HSC_PAGE_EXPIRED : return "Page Expired";
  92. case HSC_ENHANCE_YOUR_CALM : return "Enhance Your Calm";
  93. case HSC_MISDIRECTED_REQUEST : return "Misdirected Request";
  94. case HSC_UNPROCESSABLE_ENTITY : return "Unprocessable Entity";
  95. case HSC_LOCKED : return "Locked";
  96. case HSC_FAILED_DEPENDENCY : return "Failed Dependency";
  97. case HSC_UNORDERED_COLLECTION : return "Unordered Collection";
  98. case HSC_UPGRADE_REQUIRED : return "Upgrade Required";
  99. case HSC_PRECONDITION_REQUIRED : return "Precondition Required";
  100. case HSC_TOO_MANY_REQUESTS : return "Too Many Requests";
  101. case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL : return "Request Header Fields Too Large Unofficial";
  102. case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE : return "Request Header Fields Too Large";
  103. case HSC_LOGIN_TIMEOUT : return "Login Timeout";
  104. case HSC_NO_RESPONSE : return "No Response";
  105. case HSC_RETRY_WITH : return "Retry With";
  106. case HSC_BLOCKED_BY_PARENTAL_CONTROL : return "Blocked By Parental Control";
  107. case HSC_UNAVAILABLE_FOR_LEGAL_REASONS : return "Unavailable For Legal Reasons";
  108. case HSC_CLIENT_CLOSED_LOAD_BALANCED_REQUEST : return "Client Closed Load Balance Request";
  109. case HSC_INVALID_X_FORWARDED_FOR : return "Invalid X-Forwarded-For";
  110. case HSC_REQUEST_HEADER_TOO_LARGE : return "Request Header Too Large";
  111. case HSC_SSL_CERTIFICATE_ERROR : return "SSL Certificate Error";
  112. case HSC_SSL_CERTIFICATE_REQUIRED : return "SSL Certificate Required";
  113. case HSC_HTTP_REQUEST_SENT_TO_HTTPS_PORT : return "HTTP Request Sent To HTTPS Port";
  114. case HSC_INVALID_TOKEN : return "Invalid Token";
  115. case HSC_CLIENT_CLOSED_REQUEST : return "Client Closed Request";
  116. case HSC_INTERNAL_SERVER_ERROR : return "Internal Server Error";
  117. case HSC_NOT_IMPLEMENTED : return "Not Implemented";
  118. case HSC_BAD_GATEWAY : return "Bad Gateway";
  119. case HSC_SERVICE_UNAVAILABLE : return "Service Unavailable";
  120. case HSC_GATEWAY_TIMEOUT : return "Gateway Timeout";
  121. case HSC_HTTP_VERSION_NOT_SUPPORTED : return "HTTP Version Not Supported";
  122. case HSC_VARIANT_ALSO_NEGOTIATES : return "Variant Also Negotiates";
  123. case HSC_INSUFFICIENT_STORAGE : return "Insufficient Storage";
  124. case HSC_LOOP_DETECTED : return "Loop Detected";
  125. case HSC_BANDWIDTH_LIMIT_EXCEEDED : return "Bandwidth Limit Exceeded";
  126. case HSC_NOT_EXTENDED : return "Not Extended";
  127. case HSC_NETWORK_AUTHENTICATION_REQUIRED : return "Network Authentication Required";
  128. case HSC_WEB_SERVER_UNKNOWN_ERROR : return "Web Server Unknown Error";
  129. case HSC_WEB_SERVER_IS_DOWN : return "Web Server Is Down";
  130. case HSC_CONNECTION_TIMEOUT : return "Connection Timeout";
  131. case HSC_ORIGIN_IS_UNREACHABLE : return "Origin Is Unreachable";
  132. case HSC_TIMEOUT_OCCURED : return "Timeout Occured";
  133. case HSC_SSL_HANDSHAKE_FAILED : return "SSL Handshake Failed";
  134. case HSC_INVALID_SSL_CERTIFICATE : return "Invalid SSL Certificate";
  135. case HSC_RAILGUN_ERROR : return "Railgun Error";
  136. case HSC_SITE_IS_OVERLOADED : return "Site Is Overloaded";
  137. case HSC_SITE_IS_FROZEN : return "Site Is Frozen";
  138. case HSC_IDENTITY_PROVIDER_AUTHENTICATION_ERROR : return "Identity Provider Authentication Error";
  139. case HSC_NETWORK_READ_TIMEOUT : return "Network Read Timeout";
  140. case HSC_NETWORK_CONNECT_TIMEOUT : return "Network Connect Timeout";
  141. case HSC_UNPARSEABLE_RESPONSE_HEADERS : return "Unparseable Response Headers";
  142. default : return "***";
  143. }
  144. }
  145. static inline CStringA& AppendHeader(LPCSTR lpszName, LPCSTR lpszValue, CStringA& strValue)
  146. {
  147. strValue.Append(lpszName);
  148. strValue.Append(HTTP_HEADER_SEPARATOR);
  149. strValue.Append(lpszValue);
  150. strValue.Append(HTTP_CRLF);
  151. return strValue;
  152. }
  153. void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue)
  154. {
  155. ASSERT(lpszMethod);
  156. strValue.Format("%s %s HTTP/%d.%d%s", (LPCSTR)(CStringA(lpszMethod).MakeUpper()), lpszPath, LOBYTE(enVersion), HIBYTE(enVersion), HTTP_CRLF);
  157. }
  158. void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue)
  159. {
  160. if(!lpszDesc) lpszDesc = ::GetHttpDefaultStatusCodeDesc((EnHttpStatusCode)usStatusCode);
  161. strValue.Format("HTTP/%d.%d %d %s%s", LOBYTE(enVersion), HIBYTE(enVersion), usStatusCode, lpszDesc, HTTP_CRLF);
  162. }
  163. void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue)
  164. {
  165. unordered_set<LPCSTR, str_nc_hash_func::hash, str_nc_hash_func::equal_to> szHeaderNames;
  166. if(iHeaderCount > 0)
  167. {
  168. ASSERT(lpHeaders);
  169. for(int i = 0; i < iHeaderCount; i++)
  170. {
  171. const THeader& header = lpHeaders[i];
  172. ASSERT(!::IsStrEmptyA(header.name));
  173. if(!::IsStrEmptyA(header.name))
  174. {
  175. szHeaderNames.emplace(header.name);
  176. AppendHeader(header.name, header.value, strValue);
  177. }
  178. }
  179. }
  180. if( (!bRequest || iBodyLength > 0) &&
  181. (szHeaderNames.empty() ||
  182. (szHeaderNames.find(HTTP_HEADER_CONTENT_LENGTH) == szHeaderNames.end() &&
  183. szHeaderNames.find(HTTP_HEADER_TRANSFER_ENCODING) == szHeaderNames.end())))
  184. {
  185. char szBodyLength[16];
  186. itoa(iBodyLength, szBodyLength, 10);
  187. AppendHeader(HTTP_HEADER_CONTENT_LENGTH, szBodyLength, strValue);
  188. }
  189. if( (iConnFlag == 0 || iConnFlag == 1) &&
  190. (szHeaderNames.empty() ||
  191. szHeaderNames.find(HTTP_HEADER_CONNECTION) == szHeaderNames.end() ))
  192. {
  193. LPCSTR lpszValue = iConnFlag == 0 ? HTTP_CONNECTION_CLOSE_VALUE : HTTP_CONNECTION_KEEPALIVE_VALUE;
  194. AppendHeader(HTTP_HEADER_CONNECTION, lpszValue, strValue);
  195. }
  196. if( bRequest && !::IsStrEmptyA(lpszDefaultHost) &&
  197. (szHeaderNames.empty() ||
  198. (szHeaderNames.find(HTTP_HEADER_HOST) == szHeaderNames.end()) ))
  199. {
  200. CStringA strHost(lpszDefaultHost);
  201. if(usPort != 0) strHost.AppendFormat(":%u", usPort);
  202. AppendHeader(HTTP_HEADER_HOST, strHost, strValue);
  203. }
  204. szHeaderNames.clear();
  205. if(pCookies != nullptr)
  206. {
  207. DWORD dwSize = (DWORD)pCookies->size();
  208. if(dwSize > 0)
  209. {
  210. strValue.Append(HTTP_HEADER_COOKIE);
  211. strValue.Append(HTTP_HEADER_SEPARATOR);
  212. DWORD dwIndex = 0;
  213. for(TCookieMapCI it = pCookies->begin(), end = pCookies->end(); it != end; ++it, ++dwIndex)
  214. {
  215. strValue.Append(it->first);
  216. strValue.AppendChar(COOKIE_KV_SEP_CHAR);
  217. strValue.Append(it->second);
  218. if(dwIndex < dwSize - 1)
  219. strValue.Append(HTTP_COOKIE_SEPARATOR);
  220. }
  221. strValue.Append(HTTP_CRLF);
  222. }
  223. }
  224. strValue.Append(HTTP_CRLF);
  225. }
  226. void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2])
  227. {
  228. ASSERT(pBody != nullptr || iLength == 0);
  229. szBuffer[0].buf = (LPBYTE)(LPCSTR)strHeader;
  230. szBuffer[0].len = strHeader.GetLength();
  231. szBuffer[1].buf = (LPBYTE)pBody;
  232. szBuffer[1].len = iLength;
  233. }
  234. int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5])
  235. {
  236. ASSERT(iLength == 0 || pData != nullptr);
  237. int i = 0;
  238. if(::IsStrEmptyA(lpszExtensions))
  239. {
  240. sprintf(szLen, "%x" HTTP_CRLF, iLength);
  241. bufs[i].buf = (LPBYTE)szLen;
  242. bufs[i].len = (int)strlen(szLen);
  243. ++i;
  244. }
  245. else
  246. {
  247. LPCSTR lpszSep = lpszExtensions[0] == ';' ? " " : " ;";
  248. sprintf(szLen, "%x%s", iLength, lpszSep);
  249. bufs[i].buf = (LPBYTE)szLen;
  250. bufs[i].len = (int)strlen(szLen);
  251. ++i;
  252. bufs[i].buf = (LPBYTE)lpszExtensions;
  253. bufs[i].len = (int)strlen(lpszExtensions);
  254. ++i;
  255. bufs[i].buf = (LPBYTE)HTTP_CRLF;
  256. bufs[i].len = 2;
  257. ++i;
  258. }
  259. if(iLength > 0)
  260. {
  261. bufs[i].buf = (LPBYTE)pData;
  262. bufs[i].len = iLength;
  263. ++i;
  264. }
  265. bufs[i].buf = (LPBYTE)HTTP_CRLF;
  266. bufs[i].len = 2;
  267. ++i;
  268. return i;
  269. }
  270. 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])
  271. {
  272. ULONGLONG ullLength = (ULONGLONG)iLength;
  273. ASSERT(pData != nullptr || iLength == 0);
  274. ASSERT(ullBodyLen == 0 || ullBodyLen >= ullLength);
  275. if(ullBodyLen == 0)
  276. ullBodyLen = ullLength;
  277. else if(ullBodyLen < ullLength)
  278. {
  279. ::SetLastError(ERROR_INVALID_PARAMETER);
  280. return FALSE;
  281. }
  282. TBaseWSHeader bh(szHeader, TRUE);
  283. int iHeaderLen = HTTP_MIN_WS_HEADER_LEN;
  284. bh.set_fin(bFinal);
  285. bh.set_rsv(iReserved);
  286. bh.set_code(iOperationCode);
  287. bh.set_mask(lpszMask ? TRUE : FALSE);
  288. if(ullBodyLen < 126)
  289. bh.set_len((BYTE)ullBodyLen);
  290. else if(ullBodyLen <= 0xFFFF)
  291. {
  292. bh.set_len(126);
  293. bh.set_extlen((USHORT)ullBodyLen);
  294. iHeaderLen += 2;
  295. }
  296. else
  297. {
  298. bh.set_len(127);
  299. *(ULONGLONG*)(szHeader + HTTP_MIN_WS_HEADER_LEN) = ::HToN64(ullBodyLen);
  300. iHeaderLen += 8;
  301. }
  302. if(lpszMask)
  303. {
  304. memcpy(szHeader + iHeaderLen, lpszMask, 4);
  305. for(int i = 0; i < iLength; i++)
  306. pData[i] = pData[i] ^ lpszMask[i & 0x03];
  307. iHeaderLen += 4;
  308. }
  309. szBuffer[0].buf = szHeader;
  310. szBuffer[0].len = iHeaderLen;
  311. szBuffer[1].buf = (LPBYTE)pData;
  312. szBuffer[1].len = iLength;
  313. return TRUE;
  314. }
  315. BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath)
  316. {
  317. int iSchemaLength = (int)strlen(HTTP_SCHEMA);
  318. if(strnicmp(strUrl, HTTP_SCHEMA, iSchemaLength) == 0)
  319. bHttps = FALSE;
  320. else
  321. {
  322. iSchemaLength = (int)strlen(HTTPS_SCHEMA);
  323. if(strnicmp(strUrl, HTTPS_SCHEMA, iSchemaLength) == 0)
  324. bHttps = TRUE;
  325. else
  326. return FALSE;
  327. }
  328. CStringA strFullHost;
  329. int i = strUrl.Find(HTTP_PATH_SEPARATOR_CHAR, iSchemaLength);
  330. if(i > 0)
  331. {
  332. strFullHost = strUrl.Mid(iSchemaLength, i - iSchemaLength);
  333. strPath = strUrl.Mid(i);
  334. }
  335. else
  336. {
  337. strFullHost = strUrl.Mid(iSchemaLength);
  338. strPath = HTTP_PATH_SEPARATOR;
  339. }
  340. if(strFullHost.IsEmpty())
  341. return FALSE;
  342. CStringA strPort;
  343. char c = strFullHost.GetAt(0);
  344. if(!::isalnum(c))
  345. {
  346. if(c != IPV6_ADDR_BEGIN_CHAR)
  347. return FALSE;
  348. else
  349. {
  350. i = strFullHost.ReverseFind(IPV6_ADDR_END_CHAR);
  351. if(i < 0)
  352. return FALSE;
  353. else
  354. {
  355. if(strFullHost.GetLength() > i + 1)
  356. {
  357. if(strFullHost.GetAt(i + 1) != PORT_SEPARATOR_CHAR)
  358. return FALSE;
  359. strPort = strFullHost.Mid(i + 2);
  360. if(strPort.IsEmpty())
  361. return FALSE;
  362. }
  363. strHost = strFullHost.Mid(1, i - 1);
  364. }
  365. }
  366. }
  367. else
  368. {
  369. i = strFullHost.Find(PORT_SEPARATOR_CHAR);
  370. if(i < 0)
  371. strHost = strFullHost;
  372. else
  373. {
  374. strPort = strFullHost.Mid(i + 1);
  375. if(strPort.IsEmpty())
  376. return FALSE;
  377. strHost = strFullHost.Mid(0, i);
  378. }
  379. }
  380. if(strPort.IsEmpty())
  381. usPort = bHttps ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT;
  382. else
  383. usPort = (USHORT)::atoi(strPort);
  384. return TRUE;
  385. }
  386. #endif