webclient.c 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. /*
  2. * File : webclient.c
  3. * COPYRIGHT (C) 2012-2013, Shanghai Real-Thread Technology Co., Ltd
  4. *
  5. * Change Logs:
  6. * Date Author Notes
  7. * 2013-05-05 Bernard the first version
  8. * 2013-06-10 Bernard fix the slow speed issue when download file.
  9. * 2015-11-14 aozima add content_length_remainder.
  10. */
  11. #include "webclient.h"
  12. #include <string.h>
  13. #include <lwip/netdb.h>
  14. #include <lwip/sockets.h>
  15. #include "webclient_internal.h"
  16. // #define DEBUG_ENABLE
  17. #define DEBUG_SECTION_NAME "[WEB]"
  18. #define DEBUG_LEVEL DBG_LOG
  19. #define DEBUG_COLOR
  20. #include <rtdbg.h>
  21. #define WEBCLIENT_SOCKET_TIMEO 6000 /* 6 second */
  22. extern long int strtol(const char *nptr, char **endptr, int base);
  23. char *webclient_strdup(const char *s)
  24. {
  25. size_t len = strlen(s) + 1;
  26. char *tmp = (char *)web_malloc(len);
  27. if (!tmp) return NULL;
  28. memcpy(tmp, s, len);
  29. return tmp;
  30. }
  31. static char* webclient_header_skip_prefix(char* line, const char* prefix)
  32. {
  33. char *ptr;
  34. size_t len = strlen(prefix);
  35. if (strncmp(line, prefix, len))
  36. return NULL;
  37. ptr = line + len;
  38. /* skip whitespace */
  39. while (*ptr && (*ptr == ' ' || *ptr == '\t'))
  40. ptr += 1;
  41. /* remove '\r\n' */
  42. line = ptr;
  43. ptr = strstr(line, "\r\n");
  44. if (ptr != RT_NULL)
  45. {
  46. *ptr = '\0';
  47. }
  48. return line;
  49. }
  50. /*
  51. * When a request has been sent, we can expect mime headers to be
  52. * before the data. We need to read exactly to the end of the headers
  53. * and no more data. This readline reads a single char at a time.
  54. */
  55. static int webclient_read_line(int socket, char * buffer, int size)
  56. {
  57. int rc;
  58. char *ptr = buffer;
  59. int count = 0;
  60. /* Keep reading until we fill the buffer. */
  61. while (count < size)
  62. {
  63. rc = recv(socket, ptr, 1, 0);
  64. if (rc <= 0)
  65. return rc;
  66. if (*ptr == '\n')
  67. {
  68. ptr++;
  69. count++;
  70. break;
  71. }
  72. /* increment after check for cr. Don't want to count the cr. */
  73. count++;
  74. ptr++;
  75. }
  76. /* add terminate string */
  77. *ptr = '\0';
  78. dbg_log(DBG_LOG, "%s\n", buffer);
  79. return count;
  80. }
  81. /*
  82. * resolve server address
  83. * @param server the server sockaddress
  84. * @param url the input URL address, for example, http://www.rt-thread.org/index.html
  85. * @param host_addr the buffer pointer to save server host address
  86. * @param request the pointer to point the request url, for example, /index.html
  87. *
  88. * @return 0 on resolve server address OK, others failed
  89. */
  90. static int webclient_resolve_address(struct webclient_session* session, struct sockaddr_in *server,
  91. const char *url, char** request)
  92. {
  93. int rc = WEBCLIENT_OK;
  94. char *ptr;
  95. char port[6] = "80"; /* default port of 80(http) */
  96. int i = 0, is_domain;
  97. struct hostent *hptr;
  98. const char *host_addr = 0;
  99. int url_len, host_addr_len = 0;
  100. url_len = strlen(url);
  101. /* strip protocol(http) */
  102. ptr = strchr(url, ':');
  103. if (ptr != NULL)
  104. {
  105. url = ptr + 1;
  106. }
  107. /* URL must start with double forward slashes. */
  108. if ((url[0] != '/') || (url[1] != '/'))
  109. {
  110. rc = -1;
  111. goto _exit;
  112. }
  113. url += 2;
  114. is_domain = 0;
  115. i = 0;
  116. /* allow specification of port in URL like http://www.server.net:8080/ */
  117. while (*url)
  118. {
  119. if (*url == '/')
  120. {
  121. host_addr_len = url - host_addr;
  122. break;
  123. }
  124. if (*url == ':')
  125. {
  126. unsigned char w;
  127. host_addr_len = url - host_addr;
  128. for (w = 0; w < 5 && url[w + 1] != '/' && url[w + 1] != '\0'; w++)
  129. port[w] = url[w + 1];
  130. /* get port ok */
  131. port[w] = '\0';
  132. url += w + 1;
  133. break;
  134. }
  135. if ((*url < '0' || *url > '9') && *url != '.')
  136. is_domain = 1;
  137. if(host_addr == 0)
  138. {
  139. host_addr = url;
  140. //rt_kprintf("webclient_resolve_address host_addr start: %s\n", host_addr);
  141. }
  142. url++;
  143. }
  144. *request = (char*) url;
  145. if((host_addr_len < 1) || (host_addr_len > url_len) )
  146. {
  147. //rt_kprintf("webclient_resolve_address host_addr_len: %d error!\n", host_addr_len);
  148. rc = -1;
  149. goto _exit;
  150. }
  151. /* get host addr ok. */
  152. {
  153. char *host_addr_new = web_malloc(host_addr_len+1);
  154. if(!host_addr_new)
  155. {
  156. rc = -1;
  157. goto _exit;
  158. }
  159. memcpy(host_addr_new, host_addr, host_addr_len);
  160. host_addr_new[host_addr_len] = '\0';
  161. session->host = host_addr_new;
  162. //rt_kprintf("session->host: %s\n", session->host);
  163. }
  164. if (is_domain)
  165. {
  166. /* resolve the host name. */
  167. hptr = gethostbyname(session->host);
  168. if (hptr == 0)
  169. {
  170. rt_kprintf("WEBCLIENT: failed to resolve domain '%s'\n", session->host);
  171. rc = -1;
  172. goto _exit;
  173. }
  174. memcpy(&server->sin_addr, *hptr->h_addr_list, sizeof(server->sin_addr));
  175. }
  176. else
  177. {
  178. inet_aton(session->host, (struct in_addr* )&(server->sin_addr));
  179. }
  180. /* set the port */
  181. server->sin_port = htons((int) strtol(port, NULL, 10));
  182. server->sin_family = AF_INET;
  183. _exit:
  184. if(rc != WEBCLIENT_OK)
  185. {
  186. if(session->host)
  187. {
  188. web_free(session->host);
  189. session->host = 0;
  190. }
  191. }
  192. return rc;
  193. }
  194. int webclient_send_header(struct webclient_session* session, int method,
  195. const char *header, size_t header_sz)
  196. {
  197. int rc = WEBCLIENT_OK;
  198. unsigned char *header_buffer = RT_NULL, *header_ptr;
  199. if (header == RT_NULL)
  200. {
  201. header_buffer = web_malloc (WEBCLIENT_HEADER_BUFSZ);
  202. if (header_buffer == RT_NULL)
  203. {
  204. rc = -WEBCLIENT_NOMEM;
  205. goto __exit;
  206. }
  207. header_ptr = header_buffer;
  208. header_ptr += rt_snprintf((char*) header_ptr,
  209. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  210. "GET %s HTTP/1.1\r\n",
  211. session->request ? session->request : "/");
  212. header_ptr += rt_snprintf((char*) header_ptr,
  213. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  214. "Host: %s\r\n", session->host);
  215. header_ptr += rt_snprintf((char*) header_ptr,
  216. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  217. "User-Agent: RT-Thread HTTP Agent\r\n\r\n");
  218. webclient_write(session, header_buffer, header_ptr - header_buffer);
  219. }
  220. else
  221. {
  222. if (method != WEBCLIENT_USER_METHOD)
  223. {
  224. header_buffer = web_malloc (WEBCLIENT_HEADER_BUFSZ);
  225. if (header_buffer == RT_NULL)
  226. {
  227. rc = -WEBCLIENT_NOMEM;
  228. goto __exit;
  229. }
  230. header_ptr = header_buffer;
  231. if (strstr(header, "HTTP/1.") == RT_NULL)
  232. {
  233. if (method == WEBCLIENT_GET)
  234. header_ptr += rt_snprintf((char*) header_ptr,
  235. WEBCLIENT_HEADER_BUFSZ
  236. - (header_ptr - header_buffer),
  237. "GET %s HTTP/1.1\r\n",
  238. session->request ? session->request : "/");
  239. else if (method == WEBCLIENT_POST)
  240. header_ptr += rt_snprintf((char*) header_ptr,
  241. WEBCLIENT_HEADER_BUFSZ
  242. - (header_ptr - header_buffer),
  243. "POST %s HTTP/1.1\r\n",
  244. session->request ? session->request : "/");
  245. }
  246. if (strstr(header, "Host:") == RT_NULL)
  247. {
  248. header_ptr += rt_snprintf((char*) header_ptr,
  249. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  250. "Host: %s\r\n", session->host);
  251. }
  252. if (strstr(header, "User-Agent:") == RT_NULL)
  253. {
  254. header_ptr += rt_snprintf((char*) header_ptr,
  255. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  256. "User-Agent: RT-Thread HTTP Agent\r\n");
  257. }
  258. if (strstr(header, "Accept: ") == RT_NULL)
  259. {
  260. header_ptr += rt_snprintf((char*) header_ptr,
  261. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  262. "Accept: */*\r\n");
  263. }
  264. if ((WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer))
  265. < (int) header_sz + 3)
  266. {
  267. rc = -WEBCLIENT_NOBUFFER;
  268. goto __exit;
  269. }
  270. /* append user's header */
  271. memcpy(header_ptr, header, header_sz);
  272. header_ptr += header_sz;
  273. header_ptr += rt_snprintf((char*) header_ptr,
  274. WEBCLIENT_HEADER_BUFSZ - (header_ptr - header_buffer),
  275. "\r\n");
  276. webclient_write(session, header_buffer, header_ptr - header_buffer);
  277. }
  278. else
  279. {
  280. webclient_write(session, (unsigned char*) header, header_sz);
  281. }
  282. }
  283. __exit:
  284. web_free(header_buffer);
  285. return rc;
  286. }
  287. int webclient_handle_response(struct webclient_session* session)
  288. {
  289. int rc;
  290. int content_length = -1;
  291. char *mimeBuffer, *mime_ptr;
  292. if (!session) return -1;
  293. /* set content length of session */
  294. session->content_length = -1;
  295. mimeBuffer = (char*)web_malloc(WEBCLIENT_RESPONSE_BUFSZ + 1);
  296. if (!mimeBuffer)
  297. return -1;
  298. /* We now need to read the header information */
  299. while (1)
  300. {
  301. int i;
  302. /* read a line from the header information. */
  303. rc = webclient_read_line(session->socket, mimeBuffer, WEBCLIENT_RESPONSE_BUFSZ);
  304. if (rc < 0)
  305. break;
  306. /* set terminal charater */
  307. mimeBuffer[rc] = '\0';
  308. /* End of headers is a blank line. exit. */
  309. if (rc == 0)
  310. break;
  311. if ((rc == 2) && (mimeBuffer[0] == '\r'))
  312. break;
  313. mime_ptr = webclient_header_skip_prefix(mimeBuffer, "HTTP/1.");
  314. if (mime_ptr != RT_NULL)
  315. {
  316. mime_ptr += 1;
  317. while (*mime_ptr && (*mime_ptr == ' ' || *mime_ptr == '\t'))
  318. mime_ptr++;
  319. /* Terminate string after status code */
  320. for (i = 0; ((mime_ptr[i] != ' ') && (mime_ptr[i] != '\t')); i++)
  321. ;
  322. mime_ptr[i] = '\0';
  323. session->response = (int) strtol(mime_ptr, RT_NULL, 10);
  324. }
  325. mime_ptr = webclient_header_skip_prefix(mimeBuffer, "Last-Modified:");
  326. if (mime_ptr != RT_NULL)
  327. {
  328. session->last_modified = webclient_strdup(mime_ptr);
  329. }
  330. mime_ptr = webclient_header_skip_prefix(mimeBuffer,
  331. "Transfer-Encoding: ");
  332. if (mime_ptr != RT_NULL)
  333. {
  334. session->transfer_encoding = webclient_strdup(mime_ptr);
  335. }
  336. mime_ptr = webclient_header_skip_prefix(mimeBuffer, "Content-Type:");
  337. if (mime_ptr != RT_NULL)
  338. {
  339. session->content_type = webclient_strdup(mime_ptr);
  340. }
  341. mime_ptr = webclient_header_skip_prefix(mimeBuffer, "Content-Length:");
  342. if (mime_ptr != RT_NULL)
  343. {
  344. session->content_length = (int) strtol(mime_ptr, RT_NULL, 10);
  345. }
  346. mime_ptr = webclient_header_skip_prefix(mimeBuffer, "Location: ");
  347. if (mime_ptr != RT_NULL)
  348. {
  349. session->location = webclient_strdup(mime_ptr);
  350. }
  351. mime_ptr = webclient_header_skip_prefix(mimeBuffer, "Content-Range:");
  352. if (mime_ptr != RT_NULL)
  353. {
  354. char *ptr;
  355. mime_ptr = webclient_header_skip_prefix(mime_ptr, "bytes");
  356. while (*mime_ptr == ' ')
  357. mime_ptr++;
  358. session->position = atoi(mime_ptr);
  359. ptr = strstr(mime_ptr, "/");
  360. if (ptr)
  361. {
  362. ptr ++;
  363. content_length = atoi(ptr);
  364. }
  365. }
  366. }
  367. /* use the content length in content range */
  368. if (content_length != -1)
  369. session->content_length = content_length;
  370. session->content_length_remainder =
  371. (session->content_length) ? session->content_length : 0xFFFFFFFF;
  372. if (session->transfer_encoding
  373. && strcmp(session->transfer_encoding, "chunked") == 0)
  374. {
  375. /* chunk mode, we should get the first chunk size */
  376. webclient_read_line(session->socket, mimeBuffer, WEBCLIENT_RESPONSE_BUFSZ);
  377. session->chunk_sz = strtol(mimeBuffer, RT_NULL, 16);
  378. session->chunk_offset = 0;
  379. }
  380. /* release buffer */
  381. web_free(mimeBuffer);
  382. if (rc < 0)
  383. return rc;
  384. return session->response;
  385. }
  386. /*
  387. This is the main HTTP client connect work. Makes the connection
  388. and handles the protocol and reads the return headers. Needs
  389. to leave the stream at the start of the real data.
  390. */
  391. int webclient_connect(struct webclient_session* session, const char *URI)
  392. {
  393. int rc;
  394. int socket_handle;
  395. int timeout = WEBCLIENT_SOCKET_TIMEO;
  396. struct sockaddr_in server;
  397. char *request;
  398. RT_ASSERT(session != RT_NULL);
  399. /* initialize the socket of session */
  400. session->socket = -1;
  401. /* Check valid IP address and URL */
  402. rc = webclient_resolve_address(session, &server, URI, &request);
  403. if (rc != WEBCLIENT_OK)
  404. return rc;
  405. /* copy host address */
  406. if (*request)
  407. session->request = webclient_strdup(request);
  408. else
  409. session->request = RT_NULL;
  410. socket_handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
  411. if (socket_handle < 0)
  412. return -WEBCLIENT_NOSOCKET;
  413. /* set recv timeout option */
  414. setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, (void*) &timeout,
  415. sizeof(timeout));
  416. setsockopt(socket_handle, SOL_SOCKET, SO_SNDTIMEO, (void*) &timeout,
  417. sizeof(timeout));
  418. if (connect(socket_handle, (struct sockaddr *) &server,
  419. sizeof(struct sockaddr)) != 0)
  420. {
  421. /* connect failed, close socket handle */
  422. closesocket(socket_handle);
  423. return -WEBCLIENT_CONNECT_FAILED;
  424. }
  425. session->socket = socket_handle;
  426. return WEBCLIENT_OK;
  427. }
  428. struct webclient_session* webclient_open(const char* URI)
  429. {
  430. struct webclient_session* session;
  431. /* create session */
  432. session = (struct webclient_session*) web_malloc(sizeof(struct webclient_session));
  433. if (session == RT_NULL)
  434. return RT_NULL;
  435. memset(session, 0x0, sizeof(struct webclient_session));
  436. if (webclient_connect(session, URI) < 0)
  437. {
  438. /* connect to webclient server failed. */
  439. webclient_close(session);
  440. return RT_NULL;
  441. }
  442. if (webclient_send_header(session, WEBCLIENT_GET, RT_NULL, 0)
  443. != WEBCLIENT_OK)
  444. {
  445. /* connect to webclient server failed. */
  446. webclient_close(session);
  447. return RT_NULL;
  448. }
  449. /* handle the response header of webclient server */
  450. if (webclient_handle_response(session))
  451. {
  452. /* relocation */
  453. if ((session->response == 302 || session->response == 301) && session->location)
  454. {
  455. char *location = webclient_strdup(session->location);
  456. if (location)
  457. {
  458. webclient_close(session);
  459. session = webclient_open(location);
  460. web_free(location);
  461. return session;
  462. }
  463. }
  464. }
  465. /* open successfully */
  466. return session;
  467. }
  468. struct webclient_session* webclient_open_position(const char* URI, int position)
  469. {
  470. struct webclient_session* session;
  471. char *range_header;
  472. /* create session */
  473. session = (struct webclient_session*) web_malloc(sizeof(struct webclient_session));
  474. if (session == RT_NULL)
  475. return RT_NULL;
  476. memset(session, 0x0, sizeof(struct webclient_session));
  477. if (webclient_connect(session, URI) < 0)
  478. {
  479. /* connect to webclient server failed. */
  480. webclient_close(session);
  481. return RT_NULL;
  482. }
  483. range_header = (char*)web_malloc (WEBCLIENT_HEADER_BUFSZ);
  484. rt_snprintf(range_header, WEBCLIENT_HEADER_BUFSZ - 1,
  485. "Range: bytes=%d-\r\n", position);
  486. if (!range_header)
  487. goto __exit;
  488. if (webclient_send_header(session, WEBCLIENT_GET, range_header,
  489. rt_strlen(range_header)) != WEBCLIENT_OK)
  490. {
  491. /* connect to webclient server failed. */
  492. webclient_close(session);
  493. return RT_NULL;
  494. }
  495. /* handle the response header of webclient server */
  496. webclient_handle_response(session);
  497. /* relocation */
  498. if ((session->response == 302 || session->response == 301) && session->location)
  499. {
  500. char *location = webclient_strdup(session->location);
  501. if (location)
  502. {
  503. webclient_close(session);
  504. session = webclient_open_position(location, position);
  505. web_free(location);
  506. return session;
  507. }
  508. }
  509. /* open successfully */
  510. return session;
  511. __exit:
  512. if (range_header)
  513. web_free(range_header);
  514. if (session)
  515. webclient_close(session);
  516. return RT_NULL;
  517. }
  518. struct webclient_session* webclient_open_header(const char* URI, int method,
  519. const char* header, size_t header_sz)
  520. {
  521. struct webclient_session* session;
  522. /* create session */
  523. session = (struct webclient_session*) web_malloc(sizeof(struct webclient_session));
  524. if (session == RT_NULL)
  525. return RT_NULL;
  526. memset(session, 0, sizeof(struct webclient_session));
  527. if (webclient_connect(session, URI) < 0)
  528. {
  529. /* connect to webclient server failed. */
  530. webclient_close(session);
  531. return RT_NULL;
  532. }
  533. /* write request header */
  534. if (webclient_send_header(session, method, header, header_sz)
  535. != WEBCLIENT_OK)
  536. {
  537. /* send request header failed. */
  538. webclient_close(session);
  539. return RT_NULL;
  540. }
  541. /* handle the response header of webclient server */
  542. if (method == WEBCLIENT_GET)
  543. {
  544. webclient_handle_response(session);
  545. }
  546. /* open successfully */
  547. return session;
  548. }
  549. int webclient_set_timeout(struct webclient_session* session, int millisecond)
  550. {
  551. RT_ASSERT(session != RT_NULL);
  552. /* set recv timeout option */
  553. setsockopt(session->socket, SOL_SOCKET, SO_RCVTIMEO,
  554. (void*) &millisecond, sizeof(millisecond));
  555. setsockopt(session->socket, SOL_SOCKET, SO_SNDTIMEO,
  556. (void*) &millisecond, sizeof(millisecond));
  557. return 0;
  558. }
  559. static int webclient_next_chunk(struct webclient_session* session)
  560. {
  561. char line[64];
  562. int length;
  563. length = webclient_read_line(session->socket, line, sizeof(line));
  564. if (length)
  565. {
  566. if (strcmp(line, "\r\n") == 0)
  567. {
  568. length = webclient_read_line(session->socket, line, sizeof(line));
  569. if (length <= 0)
  570. {
  571. closesocket(session->socket);
  572. session->socket = -1;
  573. return length;
  574. }
  575. }
  576. }
  577. else
  578. {
  579. closesocket(session->socket);
  580. session->socket = -1;
  581. return length;
  582. }
  583. session->chunk_sz = strtol(line, RT_NULL, 16);
  584. session->chunk_offset = 0;
  585. if (session->chunk_sz == 0)
  586. {
  587. /* end of chunks */
  588. closesocket(session->socket);
  589. session->socket = -1;
  590. }
  591. return session->chunk_sz;
  592. }
  593. int webclient_read(struct webclient_session* session, unsigned char *buffer,
  594. size_t length)
  595. {
  596. int bytesRead = 0;
  597. int totalRead = 0;
  598. int left;
  599. if (!session || session->socket < 0) return -WEBCLIENT_DISCONNECT;
  600. if (length == 0) return 0;
  601. /* which is transfered as chunk mode */
  602. if (session->chunk_sz)
  603. {
  604. if (length > (session->chunk_sz - session->chunk_offset))
  605. length = session->chunk_sz - session->chunk_offset;
  606. bytesRead = recv(session->socket, buffer, length, 0);
  607. if (bytesRead <= 0)
  608. {
  609. if (errno == EWOULDBLOCK || errno == EAGAIN)
  610. {
  611. /* recv timeout */
  612. return -WEBCLIENT_TIMEOUT;
  613. }
  614. else
  615. {
  616. closesocket(session->socket);
  617. session->socket = -1;
  618. return 0;
  619. }
  620. }
  621. session->chunk_offset += bytesRead;
  622. if (session->chunk_offset >= session->chunk_sz)
  623. {
  624. webclient_next_chunk(session);
  625. }
  626. return bytesRead;
  627. }
  628. if(session->content_length > 0)
  629. {
  630. if(length > session->content_length_remainder)
  631. {
  632. length = session->content_length_remainder;
  633. }
  634. if(length == 0)
  635. {
  636. return 0;
  637. }
  638. }
  639. /*
  640. * Read until: there is an error, we've read "size" bytes or the remote
  641. * side has closed the connection.
  642. */
  643. left = length;
  644. do
  645. {
  646. bytesRead = recv(session->socket, buffer + totalRead, left, 0);
  647. if (bytesRead <= 0)
  648. {
  649. rt_kprintf("errno=%d\n", bytesRead);
  650. if (totalRead)
  651. {
  652. rt_kprintf("totalRead=%d\n", totalRead);
  653. break;
  654. }
  655. else
  656. {
  657. rt_kprintf("EWOULDBLOCK=%d, EAGAIN=%d\n", EWOULDBLOCK, EAGAIN);
  658. if (errno == EWOULDBLOCK || errno == EAGAIN)
  659. {
  660. /* recv timeout */
  661. return -WEBCLIENT_TIMEOUT;
  662. }
  663. else
  664. {
  665. closesocket(session->socket);
  666. session->socket = -1;
  667. return 0;
  668. }
  669. }
  670. }
  671. left -= bytesRead;
  672. totalRead += bytesRead;
  673. } while (left);
  674. if(session->content_length > 0)
  675. {
  676. session->content_length_remainder -= totalRead;
  677. }
  678. return totalRead;
  679. }
  680. int webclient_write(struct webclient_session* session,
  681. const unsigned char *buffer, size_t length)
  682. {
  683. int bytesWrite = 0;
  684. int totalWrite = 0;
  685. int left = length;
  686. RT_ASSERT(session != RT_NULL);
  687. if (session->socket < 0)
  688. return -WEBCLIENT_DISCONNECT;
  689. /*
  690. * Send all of data on the buffer.
  691. */
  692. do
  693. {
  694. bytesWrite = send(session->socket, buffer + totalWrite, left, 0);
  695. if (bytesWrite <= 0)
  696. {
  697. if (errno == EWOULDBLOCK || errno == EAGAIN)
  698. {
  699. /* send timeout */
  700. if (totalWrite) return totalWrite;
  701. continue;
  702. /* TODO: whether return the TIMEOUT
  703. * return -WEBCLIENT_TIMEOUT; */
  704. }
  705. else
  706. {
  707. closesocket(session->socket);
  708. session->socket = -1;
  709. if (totalWrite == 0) return -WEBCLIENT_DISCONNECT;
  710. break;
  711. }
  712. }
  713. left -= bytesWrite;
  714. totalWrite += bytesWrite;
  715. } while (left);
  716. return totalWrite;
  717. }
  718. /*
  719. * close a webclient client session.
  720. */
  721. int webclient_close(struct webclient_session* session)
  722. {
  723. RT_ASSERT(session != RT_NULL);
  724. if (session->socket >= 0)
  725. closesocket(session->socket);
  726. web_free(session->transfer_encoding);
  727. web_free(session->content_type);
  728. web_free(session->last_modified);
  729. web_free(session->host);
  730. web_free(session->request);
  731. web_free(session);
  732. return 0;
  733. }
  734. int webclient_response(struct webclient_session* session, void **response)
  735. {
  736. unsigned char* buf_ptr;
  737. unsigned char* response_buf = 0;
  738. int length, total_read = 0;
  739. if (!session || !response) return -1;
  740. *response = NULL; /* initialize response */
  741. if (session->content_length < 0) /* not content length field kind */
  742. {
  743. size_t result_sz;
  744. total_read = 0;
  745. while (1)
  746. {
  747. unsigned char* new_resp;
  748. result_sz = total_read + WEBCLIENT_RESPONSE_BUFSZ;
  749. new_resp = web_realloc(response_buf, result_sz + 1);
  750. if(!new_resp)
  751. {
  752. rt_kprintf("no memory for realloc new_resp\n");
  753. break;
  754. }
  755. response_buf = new_resp;
  756. buf_ptr = (unsigned char*) response_buf + total_read;
  757. /* read result */
  758. length = webclient_read(session, buf_ptr, result_sz - total_read);
  759. if (length <= 0)
  760. break;
  761. total_read += length;
  762. }
  763. }
  764. else
  765. {
  766. int result_sz;
  767. result_sz = session->content_length;
  768. response_buf = web_malloc(result_sz + 1);
  769. if(!response_buf) return 0;
  770. buf_ptr = (unsigned char*) response_buf;
  771. for (total_read = 0; total_read < result_sz;)
  772. {
  773. length = webclient_read(session, buf_ptr, result_sz - total_read);
  774. if (length <= 0)
  775. break;
  776. buf_ptr += length;
  777. total_read += length;
  778. }
  779. }
  780. if ((total_read == 0) && (response_buf != 0))
  781. {
  782. web_free(response_buf);
  783. response_buf = NULL;
  784. }
  785. if (response_buf)
  786. {
  787. *response = response_buf;
  788. *(response_buf + total_read) = '\0';
  789. }
  790. return total_read;
  791. }
  792. /*
  793. * High level APIs for webclient client
  794. */
  795. struct webclient_session* webclient_open_custom(const char* URI, int method,
  796. const char* header, size_t header_sz, const char* data, size_t data_sz)
  797. {
  798. int rc = 0;
  799. size_t length;
  800. struct webclient_session* session = RT_NULL;
  801. /* create session */
  802. session = (struct webclient_session*) web_malloc(sizeof(struct webclient_session));
  803. if (!session)
  804. {
  805. rc = -WEBCLIENT_NOMEM;
  806. goto _err_exit;
  807. }
  808. memset(session, 0x0, sizeof(struct webclient_session));
  809. rc = webclient_connect(session, URI);
  810. if (rc < 0)
  811. goto _err_exit;
  812. /* send header */
  813. rc = webclient_send_header(session, method, header, header_sz);
  814. if (rc < 0)
  815. goto _err_exit;
  816. /* POST data */
  817. if (data)
  818. {
  819. length = webclient_write(session, (unsigned char*) data, data_sz);
  820. if (length != length)
  821. {
  822. rt_kprintf("POST data %d:%d\n", length, data_sz);
  823. goto _err_exit;
  824. }
  825. }
  826. /* handle the response header of webclient server */
  827. webclient_handle_response(session);
  828. goto _success;
  829. _err_exit:
  830. if(session)
  831. {
  832. webclient_close(session);
  833. session = 0;
  834. }
  835. _success:
  836. return session;
  837. }
  838. int webclient_transfer(const char* URI, const char* header, size_t header_sz,
  839. const char* data, size_t data_sz, char *result, size_t result_sz)
  840. {
  841. int rc = 0;
  842. int length, total_read = 0;
  843. unsigned char* buf_ptr;
  844. struct webclient_session* session = RT_NULL;
  845. /* create session */
  846. session = (struct webclient_session*) web_malloc(sizeof(struct webclient_session));
  847. if (!session)
  848. {
  849. rc = -WEBCLIENT_NOMEM;
  850. goto __exit;
  851. }
  852. memset(session, 0x0, sizeof(struct webclient_session));
  853. rc = webclient_connect(session, URI);
  854. if (rc < 0)
  855. goto __exit;
  856. /* send header */
  857. rc = webclient_send_header(session, WEBCLIENT_POST, header, header_sz);
  858. if (rc < 0)
  859. goto __exit;
  860. /* POST data */
  861. length = webclient_write(session, (unsigned char*) data, data_sz);
  862. if (length != length)
  863. {
  864. rt_kprintf("POST data %d:%d\n", length, data_sz);
  865. goto __exit;
  866. }
  867. /* handle the response header of webclient server */
  868. webclient_handle_response(session);
  869. if (session->response != 200)
  870. {
  871. rt_kprintf("HTTP response: %d\n", session->response);
  872. goto __exit;
  873. }
  874. /* read response data */
  875. if (result == RT_NULL)
  876. goto __exit;
  877. if (session->content_length == 0)
  878. {
  879. total_read = 0;
  880. buf_ptr = (unsigned char*) result;
  881. while (1)
  882. {
  883. /* read result */
  884. length = webclient_read(session, buf_ptr + total_read,
  885. result_sz - total_read);
  886. if (length <= 0)
  887. break;
  888. buf_ptr += length;
  889. total_read += length;
  890. }
  891. }
  892. else
  893. {
  894. buf_ptr = (unsigned char*) result;
  895. for (total_read = 0; total_read < result_sz;)
  896. {
  897. length = webclient_read(session, buf_ptr, result_sz - total_read);
  898. if (length <= 0)
  899. break;
  900. buf_ptr += length;
  901. total_read += length;
  902. }
  903. }
  904. __exit:
  905. if (session != RT_NULL)
  906. webclient_close(session);
  907. if (rc < 0)
  908. return rc;
  909. return total_read;
  910. }