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

外挂编程

开发平台:

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. using namespace std;
  19. /**
  20.  * A private class used by UnixHttpReader. Most of the logic are
  21.  * contained in this class.
  22.  *
  23.  * The main reason this class exists is because it has reference counting
  24.  * support. That is necessary because it is not safe to use Curl handles
  25.  * in multiple threads, which complicates cancellation. Reference counting
  26.  * support makes it possible to do the cancellation in the Curl thread,
  27.  * while making UnixHttpReader's destructor non-blocking.
  28.  */
  29. class Private: public HttpReader {
  30. private:
  31. unsigned int refCount;
  32. HttpReaderStatus status;
  33. char *error;
  34. char errorBuffer[CURL_ERROR_SIZE];
  35. int size;
  36. /** @invariant url != NULL */
  37. char *url;
  38. char *postData;
  39. int postDataSize;
  40. /** @invariant userAgent != NULL */
  41. char *userAgent;
  42. bool mutexInitialized;
  43. pthread_mutex_t mutex;
  44. pthread_t thread;
  45. CURL *handle;
  46. string downloadBuffer;
  47. static size_t
  48. writeCallback(void *ptr, size_t size, size_t nmemb, void *user_data) {
  49. Private *self = (Private *) user_data;
  50. self->lock();
  51. self->status = HTTP_READER_DOWNLOADING;
  52. self->downloadBuffer.append(static_cast<const char *>(ptr), size * nmemb);
  53. if (self->size == -1) {
  54. double length;
  55. curl_easy_getinfo(self->handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
  56. if (length != 0) {
  57. self->size = (unsigned int) length;
  58. }
  59. }
  60. self->unlock();
  61. return size * nmemb;
  62. }
  63. /**
  64.  * @require this->size == -1 && this->status == HTTP_READER_CONNECTING
  65.  */
  66. static void *
  67. threadEntry(void *user_data) {
  68. Private *self = (Private *) user_data;
  69. self->handle = curl_easy_init();
  70. if (self->handle != NULL) {
  71. curl_easy_setopt(self->handle, CURLOPT_URL, self->url);
  72. curl_easy_setopt(self->handle, CURLOPT_FOLLOWLOCATION, 1);
  73. curl_easy_setopt(self->handle, CURLOPT_USERAGENT, self->userAgent);
  74. curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, writeCallback);
  75. curl_easy_setopt(self->handle, CURLOPT_WRITEDATA, self);
  76. curl_easy_setopt(self->handle, CURLOPT_FAILONERROR, 1);
  77. curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->errorBuffer);
  78. if (self->postData != NULL) {
  79. curl_easy_setopt(self->handle, CURLOPT_POST, 1);
  80. curl_easy_setopt(self->handle, CURLOPT_POSTFIELDS, self->postData);
  81. curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE, self->postDataSize);
  82. }
  83. if (curl_easy_perform(self->handle) == 0) {
  84. self->lock();
  85. self->status = HTTP_READER_DONE;
  86. self->unlock();
  87. } else {
  88. self->lock();
  89. self->status = HTTP_READER_ERROR;
  90. self->error = self->errorBuffer;
  91. self->size = -2;
  92. self->unlock();
  93. }
  94. } else {
  95. self->lock();
  96. self->status = HTTP_READER_ERROR;
  97. self->error = "Cannot initialize libcurl.";
  98. self->size = -2;
  99. self->unlock();
  100. }
  101. self->unref();
  102. return NULL;
  103. }
  104. void
  105. lock() const {
  106. if (mutexInitialized) {
  107. pthread_mutex_lock((pthread_mutex_t *) &mutex);
  108. }
  109. }
  110. void
  111. unlock() const {
  112. if (mutexInitialized) {
  113. pthread_mutex_unlock((pthread_mutex_t *) &mutex);
  114. }
  115. }
  116. public:
  117. Private(const char *url, const char *postData, int postDataSize, const char *userAgent) {
  118. refCount = 1;
  119. this->url = NULL;
  120. this->postData = NULL;
  121. this->userAgent = NULL;
  122. handle = NULL;
  123. status = HTTP_READER_ERROR;
  124. size = -2;
  125. if (pthread_mutex_init(&mutex, NULL) != 0) {
  126. error = "Cannot initialize mutex.";
  127. mutexInitialized = false;
  128. return;
  129. } else {
  130. mutexInitialized = true;
  131. }
  132. pthread_attr_t attr;
  133. if (pthread_attr_init(&attr) != 0) {
  134. error = "Cannot create thread attribute object.";
  135. return;
  136. }
  137. if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
  138. pthread_attr_destroy(&attr);
  139. error = "Cannot set thread attribute to detached.";
  140. return;
  141. }
  142. this->url = strdup(url);
  143. if (postData != NULL) {
  144. if (postDataSize == -1) {
  145. postDataSize = strlen(postData);
  146. }
  147. this->postData = (char *) malloc(postDataSize);
  148. if (this->postData != NULL) {
  149. memcpy(this->postData, postData, postDataSize);
  150. this->postDataSize = postDataSize;
  151. } else {
  152. error = "Cannot allocate memory for POST data.";
  153. return;
  154. }
  155. }
  156. this->userAgent = strdup(userAgent);
  157. status = HTTP_READER_CONNECTING;
  158. error = NULL;
  159. size = -1;
  160. ref();
  161. if (pthread_create(&thread, &attr, threadEntry, this) != 0) {
  162. unref();
  163. pthread_attr_destroy(&attr);
  164. status = HTTP_READER_ERROR;
  165. error = "Cannot create a thread.";
  166. size = -2;
  167. }
  168. }
  169. ~Private() {
  170. if (url != NULL)
  171. free(url);
  172. if (postData != NULL)
  173. free(postData);
  174. if (userAgent != NULL)
  175. free(userAgent);
  176. if (handle != NULL)
  177. curl_easy_cleanup(handle);
  178. if (mutexInitialized)
  179. pthread_mutex_destroy(&mutex);
  180. }
  181. /**
  182.  * Increase the reference count by 1.
  183.  */
  184. void
  185. ref() {
  186. lock();
  187. refCount++;
  188. unlock();
  189. }
  190. /**
  191.  * Decrease the reference count by 1. The object will
  192.  * be destroyed when the reference count drops to 0.
  193.  */
  194. void
  195. unref() {
  196. bool mustDelete;
  197. lock();
  198. refCount--;
  199. mustDelete = refCount == 0;
  200. unlock();
  201. if (mustDelete) {
  202. delete this;
  203. }
  204. }
  205. virtual HttpReaderStatus
  206. getStatus() const {
  207. HttpReaderStatus result;
  208. lock();
  209. result = status;
  210. unlock();
  211. return result;
  212. }
  213. virtual const char *
  214. getError() const {
  215. const char *result;
  216. lock();
  217. result = error;
  218. unlock();
  219. return result;
  220. }
  221. virtual int
  222. pullData(void *buf, unsigned int size) {
  223. int result;
  224. lock();
  225. switch (status) {
  226. case HTTP_READER_ERROR:
  227. result = -2;
  228. break;
  229. case HTTP_READER_DONE:
  230. if (downloadBuffer.empty()) {
  231. result = 0;
  232. } else {
  233. result = (downloadBuffer.size() > size) ?
  234. size :
  235. downloadBuffer.size();
  236. downloadBuffer.copy(reinterpret_cast<char *>(buf), result);
  237. downloadBuffer.erase(0, result);
  238. }
  239. break;
  240. case HTTP_READER_DOWNLOADING:
  241. if (downloadBuffer.empty()) {
  242. result = -1;
  243. } else {
  244. result = (downloadBuffer.size() > size) ?
  245. size :
  246. downloadBuffer.size();
  247. downloadBuffer.copy(reinterpret_cast<char *>(buf), result);
  248. downloadBuffer.erase(0, result);
  249. }
  250. break;
  251. default:
  252. result = -2;
  253. fprintf(stderr, "StdHttpReader: invalid status %dn", status);
  254. abort();
  255. break;
  256. };
  257. unlock();
  258. return result;
  259. }
  260. virtual const char *
  261. getData(unsigned int &len) const {
  262. len = downloadBuffer.size();
  263. return downloadBuffer.data();
  264. }
  265. virtual int
  266. getSize() const {
  267. int result;
  268. lock();
  269. result = size;
  270. unlock();
  271. return result;
  272. }
  273. };
  274. class UnixHttpReader: public StdHttpReader {
  275. private:
  276. Private *priv;
  277. public:
  278. UnixHttpReader(const char *url,
  279.        const char *postData,
  280.        int postDataSize,
  281.        const char *userAgent) {
  282. priv = new Private(url, postData, postDataSize, userAgent);
  283. }
  284. ~UnixHttpReader() {
  285. priv->unref();
  286. }
  287. virtual HttpReaderStatus
  288. getStatus() const {
  289. return priv->getStatus();
  290. }
  291. virtual const char *
  292. getError() const {
  293. assert(getStatus() == HTTP_READER_ERROR);
  294. return priv->getError();
  295. }
  296. virtual int
  297. pullData(void *buf, unsigned int size) {
  298. assert(getStatus() != HTTP_READER_CONNECTING);
  299. assert(buf != NULL);
  300. assert(size > 0);
  301. return priv->pullData(buf, size);
  302. }
  303. virtual const char *
  304. getData(unsigned int &len) const {
  305. assert(getStatus() == HTTP_READER_DONE);
  306. return priv->getData(len);
  307. }
  308. virtual int
  309. getSize() const {
  310. assert(getStatus() != HTTP_READER_CONNECTING);
  311. return priv->getSize();
  312. }
  313. static void
  314. init() {
  315. curl_global_init(CURL_GLOBAL_ALL);
  316. }
  317. };