utils_httpc.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /*
  2. * Copyright (C) 2012-2019 UCloud. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License").
  5. * You may not use this file except in compliance with the License.
  6. * A copy of the License is located at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * or in the "license" file accompanying this file. This file is distributed
  11. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. * express or implied. See the License for the specific language governing
  13. * permissions and limitations under the License.
  14. */
  15. #ifdef __cplusplus
  16. extern "C" {
  17. #endif
  18. #include <string.h>
  19. #include <stddef.h>
  20. #include <stdlib.h>
  21. #include "uiot_defs.h"
  22. #include "utils_httpc.h"
  23. #include "utils_net.h"
  24. #include "utils_timer.h"
  25. #include "utils_md5.h"
  26. #define MIN_TIMEOUT (100)
  27. #define MAX_RETRY_COUNT (600)
  28. #define HTTP_CLIENT_READ_BUF_SIZE (1024) /* read payload */
  29. #define HTTP_CLIENT_READ_HEAD_SIZE (32) /* read header */
  30. #define HTTP_CLIENT_SEND_BUF_SIZE (1024) /* send */
  31. #define HTTP_CLIENT_REQUEST_BUF_SIZE (300) /* send */
  32. #define HTTP_CLIENT_MAX_URL_LEN (256)
  33. #define HTTP_RETRIEVE_MORE_DATA (1) /**< More data needs to be retrieved. */
  34. #define HTTP_CLIENT_CHUNK_SIZE (1024)
  35. static int _utils_parse_url(const char *url, char *host, char *path) {
  36. char *host_ptr = (char *) strstr(url, "://");
  37. uint32_t host_len = 0;
  38. uint32_t path_len;
  39. /* char *port_ptr; */
  40. char *path_ptr;
  41. char *fragment_ptr;
  42. if (host_ptr == NULL) {
  43. return -1; /* URL is invalid */
  44. }
  45. host_ptr += 3;
  46. path_ptr = strchr(host_ptr, '/');
  47. if (NULL == path_ptr) {
  48. return -2;
  49. }
  50. host_len = path_ptr - host_ptr;
  51. memcpy(host, host_ptr, host_len);
  52. host[host_len] = '\0';
  53. fragment_ptr = strchr(host_ptr, '#');
  54. if (fragment_ptr != NULL) {
  55. path_len = fragment_ptr - path_ptr;
  56. } else {
  57. path_len = strlen(path_ptr);
  58. }
  59. memcpy(path, path_ptr, path_len);
  60. path[path_len] = '\0';
  61. return SUCCESS_RET;
  62. }
  63. static int _utils_fill_tx_buffer(http_client_t *client, unsigned char *send_buf, int *send_idx, char *buf, uint32_t len) {
  64. int ret;
  65. int cp_len;
  66. int writen_len = 0;
  67. int idx = *send_idx;
  68. if (len == 0) {
  69. len = strlen(buf);
  70. }
  71. do {
  72. if ((HTTP_CLIENT_SEND_BUF_SIZE - idx) >= len) {
  73. cp_len = len;
  74. } else {
  75. cp_len = HTTP_CLIENT_SEND_BUF_SIZE - idx;
  76. }
  77. memcpy(send_buf + idx, buf + writen_len, cp_len);
  78. idx += cp_len;
  79. writen_len += cp_len;
  80. len -= cp_len;
  81. if (idx == HTTP_CLIENT_SEND_BUF_SIZE) {
  82. ret = client->net.write(&client->net, send_buf, HTTP_CLIENT_SEND_BUF_SIZE, 5000);
  83. if (ret < 0) {
  84. return (ret);
  85. } else if (ret != HTTP_CLIENT_SEND_BUF_SIZE) {
  86. return (ret == 0) ? ERR_HTTP_CLOSED : ERR_HTTP_CONN_ERROR;
  87. }
  88. idx -= ret;
  89. }
  90. } while (len);
  91. *send_idx = idx;
  92. return SUCCESS_RET;
  93. }
  94. static int _http_send_header(http_client_t *client, char *host, const char *path, int method, uint32_t size_fetched, size_t range_len,
  95. http_client_data_t *client_data) {
  96. int len;
  97. unsigned char send_buf[HTTP_CLIENT_SEND_BUF_SIZE] = {0};
  98. char buf[HTTP_CLIENT_REQUEST_BUF_SIZE] = {0};
  99. char *pMethod = (method == HTTP_GET) ? "GET" : (method == HTTP_POST) ? "POST" :
  100. (method == HTTP_PUT) ? "PUT" : (method == HTTP_DELETE) ? "DELETE" :
  101. (method == HTTP_HEAD) ? "HEAD" : "";
  102. int ret;
  103. /* Send request */
  104. memset(send_buf, 0, HTTP_CLIENT_SEND_BUF_SIZE);
  105. len = 0; /* Reset send buffer */
  106. if(range_len != 0)
  107. {
  108. HAL_Snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\nRange: bytes=%d-%d\r\n", pMethod, path, host, size_fetched, size_fetched + range_len); /* Write request */
  109. }
  110. else
  111. {
  112. HAL_Snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", pMethod, path, host); /* Write request */
  113. }
  114. ret = _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
  115. if (ret < 0) {
  116. /* LOG_ERROR("Could not write request"); */
  117. return ERR_HTTP_CONN_ERROR;
  118. }
  119. /* Add user header information */
  120. if (client->header) {
  121. _utils_fill_tx_buffer(client, send_buf, &len, (char *) client->header, strlen(client->header));
  122. }
  123. if (client_data->post_buf != NULL) {
  124. HAL_Snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", client_data->post_buf_len);
  125. _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
  126. if (client_data->post_content_type != NULL) {
  127. HAL_Snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", client_data->post_content_type);
  128. _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
  129. }
  130. }
  131. /* Close headers */
  132. _utils_fill_tx_buffer(client, send_buf, &len, "\r\n", 0);
  133. ret = client->net.write(&client->net, send_buf, len, 5000);
  134. if (ret <= 0) {
  135. LOG_ERROR("ret = client->net.write() = %d", ret);
  136. return (ret == 0) ? ERR_HTTP_CLOSED : ERR_HTTP_CONN_ERROR;
  137. }
  138. return SUCCESS_RET;
  139. }
  140. int _http_send_user_data(http_client_t *client, http_client_data_t *client_data, uint32_t timeout_ms) {
  141. int ret = 0;
  142. if (client_data->post_buf && client_data->post_buf_len) {
  143. ret = client->net.write(&client->net, (unsigned char *) client_data->post_buf, client_data->post_buf_len, timeout_ms);
  144. LOG_DEBUG("client_data->post_buf: %s, ret is %d", client_data->post_buf, ret);
  145. if (ret <= 0) {
  146. return (ret == 0) ? ERR_HTTP_CLOSED : ERR_HTTP_CONN_ERROR; /* Connection was closed by server */
  147. }
  148. }
  149. return SUCCESS_RET;
  150. }
  151. /* 0 on success, err code on failure */
  152. static int _http_recv(http_client_t *client, unsigned char *buf, int max_len, int *p_read_len,
  153. uint32_t timeout_ms) {
  154. int ret = 0;
  155. *p_read_len = 0;
  156. ret = client->net.read(&client->net, buf, max_len, timeout_ms);
  157. if (ret > 0) {
  158. *p_read_len = ret;
  159. return 0;
  160. } else if (ret == 0) {
  161. /* timeout */
  162. return FAILURE_RET;
  163. } else {
  164. return ERR_HTTP_CONN_ERROR;
  165. }
  166. }
  167. static int _utils_check_deadloop(int len, Timer *timer, int ret, unsigned int *dead_loop_count,
  168. unsigned int *extend_count) {
  169. /* if timeout reduce to zero, it will be translated into NULL for select function in TLS lib */
  170. /* it would lead to indefinite behavior, so we avoid it */
  171. if (left_ms(timer) < MIN_TIMEOUT) {
  172. (*extend_count)++;
  173. countdown_ms(timer, MIN_TIMEOUT);
  174. }
  175. /* if it falls into deadloop before reconnected to internet, we just quit*/
  176. if ((0 == len) && (0 == left_ms(timer)) && (FAILURE_RET == ret)) {
  177. (*dead_loop_count)++;
  178. if (*dead_loop_count > MAX_RETRY_COUNT) {
  179. LOG_ERROR("deadloop detected, exit");
  180. return ERR_HTTP_CONN_ERROR;
  181. }
  182. } else {
  183. *dead_loop_count = 0;
  184. }
  185. /*if the internet connection is fixed during the loop, the download stream might be disconnected. we have to quit */
  186. if ((0 == len) && (*extend_count > 2 * MAX_RETRY_COUNT) && (FAILURE_RET == ret)) {
  187. LOG_ERROR("extend timer for too many times, exit");
  188. return ERR_HTTP_CONN_ERROR;
  189. }
  190. return SUCCESS_RET;
  191. }
  192. static int _utils_fill_rx_buf(int *recv_count, int len_to_write_to_response_buf, http_client_data_t *client_data,
  193. unsigned char *data) {
  194. int count = *recv_count;
  195. if (count + len_to_write_to_response_buf < client_data->response_buf_len - 1) {
  196. memcpy(client_data->response_buf + count, data, len_to_write_to_response_buf);
  197. count += len_to_write_to_response_buf;
  198. client_data->response_buf[count] = '\0';
  199. client_data->retrieve_len -= len_to_write_to_response_buf;
  200. *recv_count = count;
  201. return SUCCESS_RET;
  202. } else {
  203. memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count);
  204. client_data->response_buf[client_data->response_buf_len - 1] = '\0';
  205. client_data->retrieve_len -= (client_data->response_buf_len - 1 - count);
  206. return HTTP_RETRIEVE_MORE_DATA;
  207. }
  208. }
  209. static int _http_get_response_body(http_client_t *client, unsigned char *data, int data_len_actually_received,
  210. uint32_t timeout_ms, http_client_data_t *client_data) {
  211. int written_response_buf_len = 0;
  212. int len_to_write_to_response_buf = 0;
  213. Timer timer;
  214. init_timer(&timer);
  215. countdown_ms(&timer, timeout_ms);
  216. /* Receive data */
  217. /* LOG_DEBUG("Current data: %s", data); */
  218. client_data->is_more = 1;
  219. /* the header is not received finished */
  220. if (client_data->response_content_len == -1 && client_data->is_chunked == 0) {
  221. /* can not enter this if */
  222. LOG_ERROR("header is not received yet");
  223. return ERR_HTTP_CONN_ERROR;
  224. }
  225. while (1) {
  226. unsigned int dead_loop_count = 0;
  227. unsigned int extend_count = 0;
  228. do {
  229. int res;
  230. /* move previous fetched data into response_buf */
  231. len_to_write_to_response_buf = Min(data_len_actually_received, client_data->retrieve_len);
  232. res = _utils_fill_rx_buf(&written_response_buf_len, len_to_write_to_response_buf, client_data, data);
  233. if (HTTP_RETRIEVE_MORE_DATA == res) {
  234. return HTTP_RETRIEVE_MORE_DATA;
  235. }
  236. /* get data from internet and put into "data" buf temporary */
  237. if (client_data->retrieve_len) {
  238. int ret;
  239. int max_len_to_receive = Min(HTTP_CLIENT_CHUNK_SIZE - 1,
  240. client_data->response_buf_len - 1 - written_response_buf_len);
  241. max_len_to_receive = Min(max_len_to_receive, client_data->retrieve_len);
  242. ret = _http_recv(client, data, max_len_to_receive, &data_len_actually_received, timeout_ms);
  243. if (ret == ERR_HTTP_CONN_ERROR) {
  244. return ret;
  245. }
  246. LOG_DEBUG("Total- remaind Payload: %d Bytes; currently Read: %d Bytes", client_data->retrieve_len,
  247. data_len_actually_received);
  248. ret = _utils_check_deadloop(data_len_actually_received, &timer, ret, &dead_loop_count,
  249. &extend_count);
  250. if (ERR_HTTP_CONN_ERROR == ret) {
  251. return ret;
  252. }
  253. }
  254. } while (client_data->retrieve_len);
  255. client_data->is_more = 0;
  256. break;
  257. }
  258. return SUCCESS_RET;
  259. }
  260. static int _http_parse_response_header(http_client_t *client, char *data, int len, uint32_t timeout_ms,
  261. http_client_data_t *client_data) {
  262. int crlf_pos;
  263. char *tmp_ptr, *ptr_body_end;
  264. int new_trf_len, ret;
  265. char *crlf_ptr;
  266. client_data->response_content_len = -1;
  267. /* http client response */
  268. /* <status-line> HTTP/1.1 200 OK(CRLF)
  269. <headers> ...(CRLF)
  270. <blank line> (CRLF)
  271. [<response-body>] */
  272. crlf_ptr = strstr(data, "\r\n");
  273. if (crlf_ptr == NULL) {
  274. LOG_ERROR("\r\n not found");
  275. return ERR_HTTP_UNRESOLVED_DNS;
  276. }
  277. crlf_pos = crlf_ptr - data;
  278. data[crlf_pos] = '\0';
  279. client->response_code = atoi(data + 9);
  280. LOG_DEBUG("Reading headers: %s", data);
  281. memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1); /* Be sure to move NULL-terminating char as well */
  282. len -= (crlf_pos + 2); /* remove status_line length */
  283. client_data->is_chunked = 0;
  284. /*If not ending of response body*/
  285. /* try to read more header again until find response head ending "\r\n\r\n" */
  286. while (NULL == (ptr_body_end = strstr(data, "\r\n\r\n"))) {
  287. /* try to read more header */
  288. ret = _http_recv(client, (unsigned char *) (data + len), HTTP_CLIENT_READ_HEAD_SIZE, &new_trf_len,
  289. timeout_ms);
  290. if (ret == ERR_HTTP_CONN_ERROR) {
  291. return ret;
  292. }
  293. len += new_trf_len;
  294. data[len] = '\0';
  295. }
  296. /* parse response_content_len */
  297. if (NULL != (tmp_ptr = strstr(data, "Content-Length"))) {
  298. client_data->response_content_len = atoi(tmp_ptr + strlen("Content-Length: "));
  299. client_data->retrieve_len = client_data->response_content_len;
  300. } else {
  301. LOG_ERROR("Could not parse header");
  302. return ERR_HTTP_CONN_ERROR;
  303. }
  304. /* remove header length */
  305. /* len is Had read body's length */
  306. /* if client_data->response_content_len != 0, it is know response length */
  307. /* the remain length is client_data->response_content_len - len */
  308. len = len - (ptr_body_end + 4 - data);
  309. memmove(data, ptr_body_end + 4, len + 1);
  310. client_data->response_received_len += len;
  311. return _http_get_response_body(client, (unsigned char *) data, len, timeout_ms, client_data);
  312. }
  313. static int _http_connect(http_client_t *client) {
  314. int retry_max = 3;
  315. int retry_cnt = 1;
  316. int retry_interval = 1000;
  317. int rc = -1;
  318. do {
  319. client->net.handle = 0;
  320. LOG_DEBUG("calling TCP or TLS connect HAL for [%d/%d] iteration", retry_cnt, retry_max);
  321. rc = client->net.connect(&client->net);
  322. if (0 != rc) {
  323. client->net.disconnect(&client->net);
  324. LOG_ERROR("TCP or TLS connect failed, rc = %d", rc);
  325. HAL_SleepMs(retry_interval);
  326. continue;
  327. } else {
  328. LOG_DEBUG("rc = client->net.connect() = %d, success @ [%d/%d] iteration", rc, retry_cnt, retry_max);
  329. break;
  330. }
  331. } while (++retry_cnt <= retry_max);
  332. return SUCCESS_RET;
  333. }
  334. int _http_send_request(http_client_t *client, const char *url, HTTP_Request_Method method, uint32_t size_fetched, size_t range_len,
  335. http_client_data_t *client_data, uint32_t timeout_ms) {
  336. int ret = ERR_HTTP_CONN_ERROR;
  337. if (0 == client->net.handle) {
  338. return -1;
  339. }
  340. int rc;
  341. char host[HTTP_CLIENT_MAX_URL_LEN] = {0};
  342. char path[HTTP_CLIENT_MAX_URL_LEN] = {0};
  343. rc = _utils_parse_url(url, host, path);
  344. if (rc != SUCCESS_RET) {
  345. return rc;
  346. }
  347. ret = _http_send_header(client, host, path, method, size_fetched, range_len, client_data);
  348. if (ret != 0) {
  349. return -2;
  350. }
  351. if (method == HTTP_POST || method == HTTP_PUT) {
  352. ret = _http_send_user_data(client, client_data,timeout_ms);
  353. if (ret < 0) {
  354. ret = -3;
  355. }
  356. }
  357. return ret;
  358. }
  359. static int _http_client_recv_response(http_client_t *client, uint32_t timeout_ms, http_client_data_t *client_data) {
  360. int read_len = 0, ret = ERR_HTTP_CONN_ERROR;
  361. char buf[HTTP_CLIENT_READ_BUF_SIZE] = {0};
  362. if (0 == client->net.handle) {
  363. LOG_ERROR("no connection have been established");
  364. return ret;
  365. }
  366. if (client_data->is_more) {
  367. client_data->response_buf[0] = '\0';
  368. ret = _http_get_response_body(client, (unsigned char *) buf, read_len, timeout_ms, client_data);
  369. } else {
  370. client_data->is_more = 1;
  371. /* try to read header */
  372. ret = _http_recv(client, (unsigned char *) buf, HTTP_CLIENT_READ_HEAD_SIZE, &read_len, timeout_ms);
  373. if (ret != 0) {
  374. return ret;
  375. }
  376. buf[read_len] = '\0';
  377. if (read_len) {
  378. ret = _http_parse_response_header(client, buf, read_len, timeout_ms, client_data);
  379. }
  380. }
  381. return ret;
  382. }
  383. int http_client_connect(http_client_t *client, const char *url, int port, const char *ca_crt) {
  384. if (client->net.handle != 0) {
  385. LOG_ERROR("http client has connected to host!");
  386. return ERR_HTTP_CONN_ERROR;
  387. }
  388. int rc;
  389. char host[HTTP_CLIENT_MAX_URL_LEN] = {0};
  390. char path[HTTP_CLIENT_MAX_URL_LEN] = {0};
  391. rc = _utils_parse_url(url, host, path);
  392. if (rc != SUCCESS_RET) {
  393. return rc;
  394. }
  395. rc = utils_net_init(&client->net, host, port, SSL_CA_VERIFY_REQUIRED, ca_crt);
  396. if (rc != SUCCESS_RET) {
  397. return rc;
  398. }
  399. rc = _http_connect(client);
  400. if (rc != SUCCESS_RET) {
  401. LOG_ERROR("_http_connect error, rc = %d", rc);
  402. http_client_close(client);
  403. } else {
  404. LOG_DEBUG("http client connect success");
  405. }
  406. return rc;
  407. }
  408. int http_client_common(http_client_t *client, const char *url, int port, const char *ca_crt,
  409. HTTP_Request_Method method, http_client_data_t *client_data, uint32_t timeout_ms) {
  410. int rc;
  411. if (client->net.handle == 0) {
  412. rc = http_client_connect(client, url, port, ca_crt);
  413. if (rc != SUCCESS_RET) {
  414. return rc;
  415. }
  416. }
  417. rc = _http_send_request(client, url, method, 0, 0, client_data, timeout_ms);
  418. if (rc != SUCCESS_RET) {
  419. LOG_ERROR("http_send_request error, rc = %d", rc);
  420. http_client_close(client);
  421. return rc;
  422. }
  423. return SUCCESS_RET;
  424. }
  425. int http_client_recv_data(http_client_t *client, uint32_t timeout_ms, http_client_data_t *client_data) {
  426. int rc = SUCCESS_RET;
  427. Timer timer;
  428. init_timer(&timer);
  429. countdown_ms(&timer, timeout_ms);
  430. do
  431. {
  432. if ((NULL != client_data->response_buf)
  433. && (0 != client_data->response_buf_len)) {
  434. rc = _http_client_recv_response(client, timeout_ms, client_data);
  435. }
  436. if(client_data->is_more)
  437. {
  438. return SUCCESS_RET;
  439. }
  440. }while((rc != SUCCESS_RET) && (!has_expired(&timer)));
  441. return rc;
  442. }
  443. void http_client_close(http_client_t *client) {
  444. if (client->net.handle > 0) {
  445. client->net.disconnect(&client->net);
  446. }
  447. client->net.handle = 0;
  448. LOG_INFO("client disconnected");
  449. }
  450. void http_client_file_md5(char* file_path, char *output)
  451. {
  452. iot_md5_context ctx;
  453. utils_md5_init(&ctx);
  454. utils_md5_starts(&ctx);
  455. char *buffer = (char *)HAL_Malloc(1024);
  456. if (NULL == buffer) {
  457. return;
  458. }
  459. memset(buffer,0,1024);
  460. uint32_t count = 0;
  461. FILE *fp = fopen(file_path, "rb+");
  462. if(NULL == fp)
  463. {
  464. return;
  465. }
  466. while((count = fread(buffer,1,1024,fp))){
  467. utils_md5_update(&ctx, (unsigned char *)buffer, count);
  468. }
  469. utils_md5_finish_hb2hex(&ctx, output);
  470. utils_md5_free(&ctx);
  471. fclose(fp);
  472. HAL_Free(buffer);
  473. }
  474. void http_client_buffer_md5(char* buffer, uint32_t buffer_len, char *output)
  475. {
  476. iot_md5_context ctx;
  477. utils_md5_init(&ctx);
  478. utils_md5_starts(&ctx);
  479. utils_md5_update(&ctx, (unsigned char *)buffer, buffer_len);
  480. utils_md5_finish_hb2hex(&ctx, output);
  481. utils_md5_free(&ctx);
  482. return;
  483. }
  484. #ifdef __cplusplus
  485. }
  486. #endif