http_fetcher.cpp
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:17k
源码类别:

P2P编程

开发平台:

Visual C++

  1. /* http_fetcher.c - HTTP handling functions
  2. HTTP Fetcher 
  3. Copyright (C) 2001, 2003, 2004 Lyle Hanson (lhanson@users.sourceforge.net)
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11. Library General Public License for more details.
  12. See LICENSE file for details
  13.  */
  14. #include "stdafx.h"
  15. #include "http_fetcher.h"
  16. HttpFetcher::HttpFetcher() : DEFAULT_VERSION("1.1"), HTTP_VERSION("HTTP/1.0"), DEFAULT_USER_AGENT("Chaos Client")
  17. {
  18. errorSource = 0;
  19. http_errno = 0;
  20. errorInt = 0; /* When the error message has a %d in it,
  21. * this variable is inserted */
  22. /* Note that '%d' cannot be escaped at this time */
  23. http_errlist[HF_SUCCESS] ="成功";
  24. http_errlist[HF_METAERROR] ="内部错误.";
  25. http_errlist[HF_NULLURL] ="无内容的URL";
  26. http_errlist[HF_HEADTIMEOUT] ="超时; %d秒钟内没有收到HTTP头";
  27. http_errlist[HF_DATATIMEOUT] ="超时; %d秒钟内没有收到数据";
  28. http_errlist[HF_FRETURNCODE] ="无返回代码";
  29. http_errlist[HF_CRETURNCODE] ="无效的返回代码";
  30. http_errlist[HF_STATUSCODE] ="状态代码是: %d. rn401:需要认证, 403:无法访问, 404:没有找到, 500:服务器出错";
  31. http_errlist[HF_CONTENTLEN] ="无效的内容长度(Content-Length)";
  32. http_errlist[HF_HERROR] ="网络错误 (description unavailable)";
  33. http_errlist[HF_CANTREDIRECT] ="状态代码是: %d,但是没有"Location"项";
  34. http_errlist[HF_MAXREDIRECTS] ="已经达到最大重定向次数(%d)";
  35. }
  36. /* 
  37.  * Actually downloads the url to localFile.
  38.  * Returns size of download on success, -1 on error is set, 
  39.  */
  40. int HttpFetcher::http_fetch(const char *url_tmp, HANDLE localFile, UINT& fileSize, UINT& downloadedSize, double limitSpeed)
  41. {
  42. fd_set rfds;
  43. struct timeval tv;
  44. char headerBuf[HEADER_BUF_SIZE];
  45. char *tmp, *url, *requestBuf = NULL, *host, *charIndex;
  46. int sock, bufsize = REQUEST_BUF_SIZE;
  47. int i,
  48. ret = -1,
  49. tempSize,
  50. selectRet,
  51. found = 0, /* For redirects */
  52. redirectsFollowed = 0;
  53. downloadedSize = 0;
  54. fileSize = -1;
  55. if(url_tmp == NULL)
  56. {
  57. errorSource = FETCHER_ERROR;
  58. http_errno = HF_NULLURL;
  59. return -1;
  60. }
  61. /* Copy the url passed in into a buffer we can work with, change, etc. */
  62. url = (char*)malloc(strlen(url_tmp)+1);
  63. if(url == NULL)
  64. {
  65. errorSource = ERRNO;
  66. return -1;
  67. }
  68. strncpy(url, url_tmp, strlen(url_tmp) + 1);
  69. /* This loop allows us to follow redirects if need be.  An afterthought,
  70.  * added to provide this basic functionality.  Will hopefully be designed
  71.  * better in 2.x.x ;) */
  72. do {
  73. /* Seek to the file path portion of the url */
  74. charIndex = strstr(url, "://");
  75. if(charIndex != NULL)
  76. {
  77. /* url contains a protocol field */
  78. charIndex += strlen("://");
  79. host = charIndex;
  80. charIndex = strchr(charIndex, '/');
  81. }
  82. else
  83. {
  84. host = (char *)url;
  85. charIndex = strchr(url, '/');
  86. }
  87. /* Compose a request string */
  88. requestBuf = (char*)malloc(bufsize);
  89. if(requestBuf == NULL)
  90. {
  91. free(url);
  92. errorSource = ERRNO;
  93. return -1;
  94. }
  95. requestBuf[0] = 0;
  96. if(charIndex == NULL)
  97. {
  98. /* The url has no '/' in it, assume the user is making a root-level
  99.  * request */ 
  100. tempSize = strlen("GET /") + strlen(HTTP_VERSION) + 2;
  101. if(_checkBufSize(&requestBuf, &bufsize, tempSize) ||
  102. _snprintf(requestBuf, bufsize, "GET / %srn", HTTP_VERSION) < 0)
  103. {
  104. free(url);
  105. free(requestBuf);
  106. errorSource = ERRNO;
  107. return -1;
  108. }
  109. }
  110. else
  111. {
  112. tempSize = strlen("GET ") + strlen(charIndex) +
  113.              strlen(HTTP_VERSION) + 4;
  114.   /* + 4 is for ' ', 'r', 'n', and NULL */
  115.                                     
  116. if(_checkBufSize(&requestBuf, &bufsize, tempSize) ||
  117. _snprintf(requestBuf, bufsize, "GET %s %srn",
  118. charIndex, HTTP_VERSION) < 0)
  119. {
  120. free(url);
  121. free(requestBuf);
  122. errorSource = ERRNO;
  123. return -1;
  124. }
  125. }
  126. /* Null out the end of the hostname if need be */
  127. if(charIndex != NULL)
  128. *charIndex = 0;
  129. /* Use Host: even though 1.0 doesn't specify it.  Some servers
  130.  * won't play nice if we don't send Host, and it shouldn't
  131.  * hurt anything */
  132. ret = bufsize - strlen(requestBuf); /* Space left in buffer */
  133. tempSize = (int)strlen("Host: ") + (int)strlen(host) + 3;
  134.         /* +3 for "rn" */
  135. if(_checkBufSize(&requestBuf, &bufsize, tempSize + 128))
  136. {
  137. free(url);
  138. free(requestBuf);
  139. errorSource = ERRNO;
  140. return -1;
  141. }
  142. strcat(requestBuf, "Host: ");
  143. strcat(requestBuf, host);
  144. strcat(requestBuf, "rn");
  145. tempSize = (int)strlen("User-Agent: ") +
  146. (int)strlen(DEFAULT_USER_AGENT) + (int)strlen(DEFAULT_VERSION) + 4;
  147.         /* + 4 is for '', 'r', 'n', and NULL */
  148. if(_checkBufSize(&requestBuf, &bufsize, tempSize))
  149. {
  150. free(url);
  151. free(requestBuf);
  152. errorSource = ERRNO;
  153. return -1;
  154. }
  155. strcat(requestBuf, "User-Agent: ");
  156. strcat(requestBuf, DEFAULT_USER_AGENT);
  157. strcat(requestBuf, "/");
  158. strcat(requestBuf, DEFAULT_VERSION);
  159. strcat(requestBuf, "rn");
  160. tempSize = (int)strlen("Connection: Closernrn");
  161. if(_checkBufSize(&requestBuf, &bufsize, tempSize))
  162. {
  163. free(url);
  164. free(requestBuf);
  165. errorSource = ERRNO;
  166. return -1;
  167. }
  168. strcat(requestBuf, "Connection: Closernrn");
  169. /* Now free any excess memory allocated to the buffer */
  170. tmp = (char*)realloc(requestBuf, strlen(requestBuf) + 1);
  171. if(tmp == NULL)
  172. {
  173. free(url);
  174. free(requestBuf);
  175. errorSource = ERRNO;
  176. return -1;
  177. }
  178. requestBuf = tmp;
  179.   sock = makeSocket(host); /* errorSource set within makeSocket */
  180. if(sock == -1) { free(url); free(requestBuf); return -1;}
  181. free(url);
  182.         url = NULL;
  183. if(send(sock, requestBuf, strlen(requestBuf), 0) == -1) // added by jarjar
  184. //if(write(sock, requestBuf, strlen(requestBuf)) == -1) // removed by jarjar
  185. {
  186. closesocket(sock); // added by jarjar
  187. //close(sock);  // removed by jarjar
  188. free(requestBuf);
  189. errorSource = ERRNO;
  190. return -1;
  191. }
  192. free(requestBuf);
  193.         requestBuf = NULL;
  194. /* Grab enough of the response to get the metadata */
  195. ret = _http_read_header(sock, headerBuf); /* errorSource set within */
  196. if(ret < 0) 
  197. closesocket(sock); // added by jarjar
  198. //close(sock);  // removed by jarjar
  199. return -1; 
  200. }
  201. /* Get the return code */
  202. charIndex = strstr(headerBuf, "HTTP/");
  203. if(charIndex == NULL)
  204. {
  205. closesocket(sock); // added by jarjar
  206. //close(sock);  // removed by jarjar
  207. errorSource = FETCHER_ERROR;
  208. http_errno = HF_FRETURNCODE;
  209. return -1;
  210. }
  211. while(*charIndex != ' ')
  212. charIndex++;
  213. charIndex++;
  214. ret = sscanf(charIndex, "%d", &i);
  215. if(ret != 1)
  216. {
  217. closesocket(sock); // added by jarjar
  218. //close(sock);  // removed by jarjar
  219. errorSource = FETCHER_ERROR;
  220. http_errno = HF_CRETURNCODE;
  221. return -1;
  222. }
  223. if(i<200 || i>307)
  224. {
  225. closesocket(sock); // added by jarjar
  226. //close(sock);  // removed by jarjar
  227. errorInt = i; /* Status code, to be inserted in error string */
  228. errorSource = FETCHER_ERROR;
  229. http_errno = HF_STATUSCODE;
  230. return -1;
  231. }
  232. /* If a redirect, repeat operation until final URL is found or we
  233.  *  redirect DEFAULT_REDIRECTS times.  Note the case sensitive "Location",
  234.  *  should probably be made more robust in the future (without relying
  235.  *  on the non-standard strcasecmp()).
  236.  * This bit mostly by Dean Wilder, tweaked by me */
  237. if(i >= 300)
  238. {
  239.     redirectsFollowed++;
  240. /* Pick up redirect URL, allocate new url, and repeat process */
  241. charIndex = strstr(headerBuf, "Location:");
  242. if(!charIndex)
  243. {
  244. closesocket(sock); // added by jarjar
  245. //close(sock);  // removed by jarjar
  246. errorInt = i; /* Status code, to be inserted in error string */
  247. errorSource = FETCHER_ERROR;
  248. http_errno = HF_CANTREDIRECT;
  249. return -1;
  250. }
  251. charIndex += strlen("Location:");
  252.             /* Skip any whitespace... */
  253.             while(*charIndex != '' && isspace(*charIndex))
  254.                 charIndex++;
  255.             if(*charIndex == '')
  256.                 {
  257. closesocket(sock); // added by jarjar
  258. //close(sock);  // removed by jarjar
  259. errorInt = i; /* Status code, to be inserted in error string */
  260. errorSource = FETCHER_ERROR;
  261. http_errno = HF_CANTREDIRECT;
  262. return -1;
  263.                 }
  264. i = strcspn(charIndex, " rn");
  265. if(i > 0)
  266. {
  267. url = (char *)malloc(i + 1);
  268. strncpy(url, charIndex, i);
  269. url[i] = '';
  270. }
  271. else
  272.                 /* Found 'Location:' but contains no URL!  We'll handle it as
  273.                  * 'found', hopefully the resulting document will give the user
  274.                  * a hint as to what happened. */
  275.                 found = 1;
  276.             }
  277. else
  278. found = 1;
  279.     } while(!found && redirectsFollowed <= DEFAULT_REDIRECTS);
  280.     if(url) /* Redirection code may malloc this, then exceed DEFAULT_REDIRECTS */
  281.         {
  282.         free(url);
  283.         url = NULL;
  284.         }
  285.     
  286.     if(redirectsFollowed >= DEFAULT_REDIRECTS && !found)
  287.         {
  288. closesocket(sock); // added by jarjar
  289. //close(sock);  // removed by jarjar
  290.      errorInt = DEFAULT_REDIRECTS; /* To be inserted in error string */
  291.      errorSource = FETCHER_ERROR;
  292.      http_errno = HF_MAXREDIRECTS;
  293.     return -1;
  294.         }
  295. /*
  296.  * Parse out about how big the data segment is.
  297.  * Note that under current HTTP standards (1.1 and prior), the
  298.  * Content-Length field is not guaranteed to be accurate or even present. 
  299.  * I just use it here so I can allocate a ballpark amount of memory.
  300.  *
  301.  * Note that some servers use different capitalization
  302.  */
  303. charIndex = strstr(headerBuf, "Content-Length:");
  304. if(charIndex == NULL)
  305. charIndex = strstr(headerBuf, "Content-length:");
  306. if(charIndex != NULL)
  307. {
  308. ret = sscanf(charIndex + strlen("content-length: "), "%d",
  309. &fileSize);
  310. if(ret < 1)
  311. {
  312. closesocket(sock); // added by jarjar
  313. //close(sock);  // removed by jarjar
  314. errorSource = FETCHER_ERROR;
  315. http_errno = HF_CONTENTLEN;
  316. return -1;
  317. }
  318. }
  319. char tmpReadBuf[BLOCK_SIZE];
  320. DWORD tmpWriteBytes = 0;
  321. tv.tv_sec = DEFAULT_READ_TIMEOUT; 
  322. tv.tv_usec = 0;
  323. DWORD dwStartTicks = GetTickCount();
  324. /* Begin reading the body of the file */
  325. while(ret > 0 && (fileSize == -1 || downloadedSize < fileSize) && localFile != INVALID_HANDLE_VALUE)
  326. {
  327. FD_ZERO(&rfds);
  328. FD_SET((SOCKET)sock, &rfds);
  329. selectRet = select(sock+1, &rfds, NULL, NULL, &tv);
  330. if(selectRet == 0)
  331. {
  332. // timeout means nothing
  333. continue;
  334. /*
  335. errorSource = FETCHER_ERROR;
  336. http_errno = HF_DATATIMEOUT;
  337. errorInt = DEFAULT_READ_TIMEOUT;
  338. closesocket(sock); // added by jarjar
  339. //close(sock);  // removed by jarjar
  340. return -1;
  341. */
  342. }
  343. else if(selectRet == -1)
  344. {
  345. closesocket(sock); // added by jarjar
  346. //close(sock);  // removed by jarjar
  347. errorSource = ERRNO;
  348. return -1;
  349. }
  350. ret = recv(sock, tmpReadBuf, BLOCK_SIZE, 0); // added by jarjar 
  351. if(ret == -1)
  352. {
  353. closesocket(sock); // added by jarjar
  354. //close(sock);  // removed by jarjar
  355. errorSource = ERRNO;
  356. return -1;
  357. }
  358. if(INVALID_SET_FILE_POINTER == SetFilePointer(localFile, downloadedSize, 0, FILE_BEGIN))
  359. {
  360. int xxx = GetLastError();
  361. closesocket(sock); // added by jarjar
  362. //close(sock);  // removed by jarjar
  363. errorSource = ERRNO;
  364. return -1;
  365. }
  366. downloadedSize += ret;
  367. if(!WriteFile(localFile, tmpReadBuf, ret, &tmpWriteBytes, NULL) || tmpWriteBytes != ret)
  368. {
  369. int xxx = GetLastError();
  370. closesocket(sock); // added by jarjar
  371. //close(sock);  // removed by jarjar
  372. errorSource = ERRNO;
  373. return -1;
  374. }
  375.         // For bandwidth throttling
  376. if (limitSpeed > 0.0f) 
  377. {
  378. double t = (double)(GetTickCount() - dwStartTicks);
  379. double q = (double)((double)downloadedSize / t);
  380. if (q > limitSpeed)
  381. Sleep((DWORD)((((q*t)/limitSpeed)-t)));
  382. }
  383. }
  384. ::FlushFileBuffers(localFile);
  385. closesocket(sock); // added by jarjar
  386. //close(sock);  // removed by jarjar
  387. return downloadedSize;
  388. }
  389. /* 
  390.  * Returns a pointer to the current error description message. The
  391.  * message pointed to is only good until the next call to http_strerror(),
  392.  * so if you need to hold on to the message for a while you should make
  393.  * a copy of it
  394.  */
  395. const char* HttpFetcher::http_strerror()
  396. {
  397. int errno;
  398. if(errorSource == ERRNO)
  399. return strerror(errno);
  400. else if(errorSource == FETCHER_ERROR)
  401. {
  402. if(strstr(http_errlist[http_errno], "%d") == NULL)
  403. return http_errlist[http_errno];
  404. else
  405. {
  406. /* The error string has a %d in it, we need to insert errorInt.
  407.  * convertedError[128] has been declared for that purpose */
  408. char *stringIndex, *originalError;
  409. originalError = (char *)http_errlist[http_errno];
  410. convertedError[0] = 0; /* Start off with NULL */
  411. stringIndex = strstr(originalError, "%d");
  412. strncat(convertedError, originalError, /* Copy up to %d */
  413. abs(stringIndex - originalError));
  414. sprintf(&convertedError[strlen(convertedError)],"%d",errorInt);
  415. stringIndex += 2; /* Skip past the %d */
  416. strcat(convertedError, stringIndex);
  417. return convertedError;
  418. }
  419. }
  420. return http_errlist[HF_METAERROR]; /* Should NEVER happen */
  421. }
  422. /*
  423.  * Reads the metadata of an HTTP response.
  424.  * Perhaps a little inefficient, as it reads 1 byte at a time, but
  425.  * I don't think it's that much of a loss (most headers aren't HUGE).
  426.  * Returns:
  427.  * # of bytes read on success, or
  428.  * -1 on error
  429.  */
  430. int HttpFetcher::_http_read_header(int sock, char *headerPtr)
  431. {
  432. fd_set rfds;
  433. struct timeval tv;
  434. int bytesRead = 0, newlines = 0, ret, selectRet;
  435. while(newlines != 2 && bytesRead != HEADER_BUF_SIZE)
  436. {
  437. FD_ZERO(&rfds);
  438. FD_SET((SOCKET)sock, &rfds);
  439. tv.tv_sec = DEFAULT_READ_TIMEOUT; 
  440. tv.tv_usec = 0;
  441. selectRet = select(sock+1, &rfds, NULL, NULL, &tv);
  442. if(selectRet == 0)
  443. {
  444. errorSource = FETCHER_ERROR;
  445. http_errno = HF_HEADTIMEOUT;
  446. errorInt = DEFAULT_READ_TIMEOUT;
  447. return -1;
  448. }
  449. else if(selectRet == -1) { errorSource = ERRNO; return -1; }
  450. ret = recv(sock, headerPtr, 1, 0);
  451. //ret = read(sock, headerPtr, 1);  //removed by jarjar
  452. if(ret == -1) { errorSource = ERRNO; return -1; }
  453. bytesRead++;
  454. if(*headerPtr == 'r') /* Ignore CR */
  455. {
  456. /* Basically do nothing special, just don't set newlines
  457.  * to 0 */
  458. headerPtr++;
  459. continue;
  460. }
  461. else if(*headerPtr == 'n') /* LF is the separator */
  462. newlines++;
  463. else
  464. newlines = 0;
  465. headerPtr++;
  466. }
  467. headerPtr -= 3; /* Snip the trailing LF's */
  468. *headerPtr = '';
  469. return bytesRead;
  470. }
  471. /*
  472.  * Opens a TCP socket and returns the descriptor
  473.  * Returns:
  474.  * socket descriptor, or
  475.  * -1 on error
  476.  */
  477. int HttpFetcher::makeSocket(const char *host)
  478. {
  479. int sock; /* Socket descriptor */
  480. struct sockaddr_in sa; /* Socket address */
  481. struct hostent *hp; /* Host entity */
  482. int ret;
  483. u_short port; // added by jarjar
  484.     //int port; // removed by jarjar
  485.     char *p;
  486.     /* Check for port number specified in URL */
  487.     p = strchr(host, ':');
  488.     if(p)
  489.         {
  490.         port = atoi(p + 1);
  491.         *p = '';
  492.         }
  493.     else
  494.         port = PORT_NUMBER;
  495. hp = gethostbyname(host);
  496. if(hp == NULL) { errorSource = ERRNO; return -1; }
  497. /* Copy host address from hostent to (server) socket address */
  498. memcpy((char *)&sa.sin_addr, (char *)hp->h_addr, hp->h_length);
  499. sa.sin_family = hp->h_addrtype; /* Set service sin_family to PF_INET */
  500. sa.sin_port = htons(port);       /* Put portnum into sockaddr */
  501. sock = socket(hp->h_addrtype, SOCK_STREAM, 0);
  502. if(sock == -1) { errorSource = ERRNO; return -1; }
  503. ret = connect(sock, (struct sockaddr *)&sa, sizeof(sa));
  504. if(ret == -1) { errorSource = ERRNO; return -1; }
  505. return sock;
  506. }
  507. /*
  508.  * Determines if the given NULL-terminated buffer is large enough to
  509.  *  concatenate the given number of characters.  If not, it attempts to
  510.  *  grow the buffer to fit.
  511.  * Returns:
  512.  * 0 on success, or
  513.  * -1 on error (original buffer is unchanged).
  514.  */
  515. int HttpFetcher::_checkBufSize(char **buf, int *bufsize, int more)
  516. {
  517. char *tmp;
  518. int roomLeft = *bufsize - (strlen(*buf) + 1);
  519. if(roomLeft > more)
  520. return 0;
  521. tmp = (char*)realloc(*buf, *bufsize + more + 1);
  522. if(tmp == NULL)
  523. return -1;
  524. *buf = tmp;
  525. *bufsize += more + 1;
  526. return 0;
  527. }