http-reader.cpp
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:11k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. /*  Asynchronous HTTP client
  2.  *  Copyright (C) 2006   Written by VCL
  3.  *
  4.  *  This program is free software; you can redistribute it and/or modify
  5.  *  it under the terms of the GNU General Public License as published by
  6.  *  the Free Software Foundation; either version 2 of the License, or
  7.  *  (at your option) any later version.
  8.  *
  9.  *  This program is distributed in the hope that it will be useful,
  10.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  *  GNU General Public License for more details.
  13.  *
  14.  *  You should have received a copy of the GNU General Public License
  15.  *  along with this program; if not, write to the Free Software
  16.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  17.  */
  18. class WinHttpReader: public StdHttpReader {
  19. private:
  20. HINTERNET inetHandle;
  21. HINTERNET connectHandle;
  22. HINTERNET openHandle;
  23. HANDLE threadHandle;
  24. char *postData;
  25. int postDataSize;
  26. CRITICAL_SECTION lock;
  27. std::string downloadBuffer;
  28. HttpReaderStatus status;
  29. char *error;
  30. bool errorMustBeFreed;
  31. int size;
  32. /**
  33.  * Copy a part of a string.
  34.  *
  35.  * @param str   A NULL-terminated string to copy.
  36.  * @param size  The maximum number of bytes to copy.
  37.  * @return A copy, which must be freed when no longer necessary.
  38.  *         This string is guaranteed to be NULL-terminated.
  39.  */
  40. char *
  41. strndup(const char *str, size_t size) {
  42. char *result = (char *) NULL;
  43. size_t len;
  44. if (str == (const char *) NULL)
  45. return (char *) NULL;
  46. len = strlen(str);
  47. if (len == 0)
  48. return strdup("");
  49. if (size > len)
  50. size = len;
  51. result = (char *) malloc(len + 1);
  52. memcpy(result, str, size);
  53. result[size] = '';
  54. return result;
  55. }
  56. /**
  57.  * Split an URL into smaller components.
  58.  *
  59.  * host and uri will contain newly allocated strings, which must
  60.  * be freed when no longer necessary.
  61.  *
  62.  * @param url     [in]  The URL to split.
  63.  * @param scheme  [out] The URL scheme (protocol).
  64.  * @param host    [out] A pointer to a string, which will contain the host name.
  65.  * @param port    [out] The port number.
  66.  * @param uri     [out] A pointer to a string, which will contain the URI.
  67.  *                      (e.g. "/foo/index.html")
  68.  * @return Whether the URL can be successfully splitted. If not, the URL is
  69.  *         probably invalid.
  70.  * @require
  71.  *     url != NULL && host != NULL && uri != NULL
  72.  * @ensure
  73.  *     if result: *host != NULL && *uri != NULL
  74.  */
  75. bool
  76. splitURL(const char *url, INTERNET_SCHEME &scheme, char **host,
  77.  unsigned short &port, char **uri) {
  78. URL_COMPONENTS components;
  79. components.dwStructSize = sizeof(URL_COMPONENTS);
  80. components.lpszScheme = NULL;
  81. components.dwSchemeLength = 1;
  82. components.lpszHostName = NULL;
  83. components.dwHostNameLength = 1;
  84. components.lpszUserName = NULL;
  85. components.dwUserNameLength = 0;
  86. components.lpszPassword = NULL;
  87. components.dwPasswordLength = 0;
  88. components.lpszUrlPath = NULL;
  89. components.dwUrlPathLength = 1;
  90. components.lpszExtraInfo = NULL;
  91. components.dwExtraInfoLength = 0;
  92. if (InternetCrackUrl(url, 0, 0, &components)) {
  93. scheme = components.nScheme;
  94. *host = strndup(components.lpszHostName, components.dwHostNameLength);
  95. port = components.nPort;
  96. *uri = strndup(components.lpszUrlPath, components.dwUrlPathLength);
  97. return true;
  98. } else {
  99. return false;
  100. }
  101. }
  102. /**
  103.  * The entry point for the thread, in which blocking operations
  104.  * (such as connecting and downloading) are performed.
  105.  */
  106. static DWORD WINAPI threadEntry(LPVOID param) {
  107. WinHttpReader *self = (WinHttpReader *) param;
  108. // Send HTTP request
  109. self->status = HTTP_READER_CONNECTING;
  110. if (!HttpSendRequest(self->openHandle, NULL, 0, self->postData, self->postDataSize)) {
  111. DWORD code;
  112. char buf[1024];
  113. code = GetLastError();
  114. EnterCriticalSection(&self->lock);
  115. self->status = HTTP_READER_ERROR;
  116. if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, 0, buf,
  117.     sizeof(buf), NULL) == 0) {
  118. if (code == 12045) {
  119. strncpy(buf, "Invalid server certificate.", sizeof(buf));
  120. } else {
  121. snprintf(buf, sizeof(buf), "Unable to send a HTTP request: error code %u",
  122. (unsigned int) code);
  123. }
  124. }
  125. self->error = strdup(buf);
  126. self->errorMustBeFreed = true;
  127. LeaveCriticalSection(&self->lock);
  128. return 0;
  129. }
  130. // Query HTTP status code
  131. DWORD header;
  132. DWORD headerLength = sizeof(DWORD);
  133. if (HttpQueryInfo(self->openHandle,
  134. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  135. &header, &headerLength, 0)) {
  136. if (header != 200) {
  137. EnterCriticalSection(&self->lock);
  138. self->status = HTTP_READER_ERROR;
  139. self->error = "HTTP server returned error status.";
  140. LeaveCriticalSection(&self->lock);
  141. return 0;
  142. }
  143. } else {
  144. EnterCriticalSection(&self->lock);
  145. self->status = HTTP_READER_ERROR;
  146. self->error = "Cannot query HTTP status.";
  147. LeaveCriticalSection(&self->lock);
  148. return 0;
  149. }
  150. // Query Content-Length header
  151. headerLength = sizeof(DWORD);
  152. if (HttpQueryInfo(self->openHandle,
  153.   HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
  154.   &header, &headerLength, 0)) {
  155. self->size = header;
  156. }
  157. // Start downloading data
  158. self->status = HTTP_READER_DOWNLOADING;
  159. BOOL success;
  160. char buf[1024 * 32];
  161. DWORD bytesRead;
  162. do {
  163. success = InternetReadFile(self->openHandle, buf,
  164.    sizeof(buf), &bytesRead);
  165. if (bytesRead > 0) {
  166. EnterCriticalSection(&self->lock);
  167. self->downloadBuffer.append(buf, bytesRead);
  168. LeaveCriticalSection(&self->lock);
  169. }
  170. } while (success && bytesRead != 0);
  171. if (success) {
  172. self->status = HTTP_READER_DONE;
  173. } else {
  174. EnterCriticalSection(&self->lock);
  175. self->status = HTTP_READER_ERROR;
  176. self->error = "Download failed.";
  177. LeaveCriticalSection(&self->lock);
  178. }
  179. return 0;
  180. }
  181. public:
  182. WinHttpReader(const char *url,
  183.       const char *postData,
  184.       int postDataSize,
  185.       const char *userAgent) {
  186. connectHandle = NULL;
  187. openHandle = NULL;
  188. threadHandle = NULL;
  189. status = HTTP_READER_ERROR;
  190. error = NULL;
  191. errorMustBeFreed = false;
  192. size = -2;
  193. this->postData = NULL;
  194. this->postDataSize = 0;
  195. InitializeCriticalSection(&lock);
  196. inetHandle = InternetOpen(userAgent, INTERNET_OPEN_TYPE_PRECONFIG,
  197.       NULL, NULL, 0);
  198. if (inetHandle == NULL) {
  199. error = "Cannot initialize the Internet library.";
  200. return;
  201. }
  202. INTERNET_SCHEME scheme;
  203. char *host, *uri, *method;
  204. unsigned short port;
  205. if (!splitURL(url, scheme, &host, port, &uri)) {
  206. error = "Invalid URL.";
  207. return;
  208. }
  209. assert(host != NULL);
  210. assert(uri != NULL);
  211. connectHandle = InternetConnect(inetHandle, host, port, NULL, NULL,
  212. INTERNET_SERVICE_HTTP, 0, 0);
  213. if (connectHandle == NULL) {
  214. error = "Cannot initialize an Internet connection object.";
  215. free(host);
  216. free(uri);
  217. return;
  218. }
  219. DWORD flags = INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_DONT_CACHE
  220. | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES
  221. | INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE
  222. | INTERNET_FLAG_RELOAD;
  223. if (scheme == INTERNET_SCHEME_HTTPS) {
  224. flags |= INTERNET_FLAG_SECURE;
  225. }
  226. if (postData == NULL) {
  227. method = "GET";
  228. } else {
  229. method = "POST";
  230. }
  231. openHandle = HttpOpenRequest(connectHandle, method, uri, "HTTP/1.1",
  232.      NULL, NULL, flags, 0);
  233. free(host);
  234. free(uri);
  235. if (openHandle == NULL) {
  236. error = "Cannot open a HTTP request.";
  237. return;
  238. }
  239. if (postData != NULL) {
  240. const char *header = "Content-Type: application/x-www-form-urlencodedrn";
  241. if (!HttpAddRequestHeaders(openHandle, header, strlen(header),
  242.     HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDREQ_FLAG_ADD)) {
  243. error = "Cannot add Content-Type HTTP request header.";
  244. return;
  245. }
  246. if (postDataSize == -1) {
  247. postDataSize = strlen(postData);
  248. }
  249. this->postDataSize = postDataSize;
  250. this->postData = (char *) malloc(postDataSize);
  251. if (this->postData == NULL) {
  252. error = "Cannot allocate memory for HTTP POST data.";
  253. return;
  254. }
  255. memcpy(this->postData, postData, postDataSize);
  256. }
  257. // Ignore invalid SSL certificates.
  258. DWORD len = sizeof(flags);
  259. InternetQueryOption(openHandle, INTERNET_OPTION_SECURITY_FLAGS,
  260. (LPVOID) &flags, &len);
  261. flags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA;
  262. InternetSetOption(openHandle, INTERNET_OPTION_SECURITY_FLAGS,
  263. &flags, sizeof(flags));
  264. status = HTTP_READER_CONNECTING;
  265. size = -1;
  266. DWORD threadID;
  267. threadHandle = CreateThread(NULL, 0, threadEntry, this, 0, &threadID);
  268. if (threadHandle == NULL) {
  269. error = "Cannot create a thread.";
  270. return;
  271. }
  272. if (errorMustBeFreed && error != NULL)
  273. free(error);
  274. }
  275. ~WinHttpReader() {
  276. if (inetHandle != NULL)
  277. InternetCloseHandle(inetHandle);
  278. if (connectHandle != NULL)
  279. InternetCloseHandle(connectHandle);
  280. if (openHandle != NULL)
  281. InternetCloseHandle(openHandle);
  282. if (threadHandle) {
  283. WaitForSingleObject(threadHandle, INFINITE);
  284. CloseHandle(threadHandle);
  285. }
  286. if (this->postData != NULL)
  287. free(this->postData);
  288. }
  289. virtual HttpReaderStatus
  290. getStatus() const {
  291. HttpReaderStatus result;
  292. EnterCriticalSection((CRITICAL_SECTION *) &lock);
  293. result = status;
  294. LeaveCriticalSection((CRITICAL_SECTION *) &lock);
  295. return result;
  296. }
  297. virtual const char *
  298. getError() const {
  299. assert(status == HTTP_READER_ERROR);
  300. const char *result;
  301. EnterCriticalSection((CRITICAL_SECTION *) &lock);
  302. result = error;
  303. LeaveCriticalSection((CRITICAL_SECTION *) &lock);
  304. return result;
  305. }
  306. virtual int
  307. pullData(void *buf, unsigned int size) {
  308. assert(status != HTTP_READER_CONNECTING);
  309. assert(buf != NULL);
  310. assert(size > 0);
  311. int result;
  312. EnterCriticalSection(&lock);
  313. switch (status) {
  314. case HTTP_READER_ERROR:
  315. result = -2;
  316. break;
  317. case HTTP_READER_DONE:
  318. if (downloadBuffer.empty()) {
  319. result = 0;
  320. } else {
  321. result = (downloadBuffer.size() > size) ?
  322. size :
  323. downloadBuffer.size();
  324. downloadBuffer.copy(reinterpret_cast<char *>(buf), result);
  325. downloadBuffer.erase(0, result);
  326. }
  327. break;
  328. case HTTP_READER_DOWNLOADING:
  329. if (downloadBuffer.empty()) {
  330. result = -1;
  331. } else {
  332. result = (downloadBuffer.size() > size) ?
  333. size :
  334. downloadBuffer.size();
  335. downloadBuffer.copy(reinterpret_cast<char *>(buf), result);
  336. downloadBuffer.erase(0, result);
  337. }
  338. break;
  339. default:
  340. result = -2;
  341. fprintf(stderr, "StdHttpReader: invalid status %dn", status);
  342. abort();
  343. break;
  344. };
  345. LeaveCriticalSection(&lock);
  346. return result;
  347. }
  348. virtual const char *
  349. getData(unsigned int &len) const {
  350. assert(status == HTTP_READER_DONE);
  351. const char *result;
  352. EnterCriticalSection((CRITICAL_SECTION *) &lock);
  353. len = downloadBuffer.size();
  354. result = downloadBuffer.c_str();
  355. LeaveCriticalSection((CRITICAL_SECTION *) &lock);
  356. return result;
  357. }
  358. virtual int
  359. getSize() const {
  360. assert(status != HTTP_READER_CONNECTING);
  361. int result;
  362. EnterCriticalSection((CRITICAL_SECTION *) &lock);
  363. if (status == HTTP_READER_ERROR) {
  364. result = -2;
  365. } else {
  366. result = size;
  367. }
  368. LeaveCriticalSection((CRITICAL_SECTION *) &lock);
  369. return result;
  370. }
  371. };