httpc.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * Copyright (C) 2019-2020 Alibaba Group Holding Limited
  3. */
  4. #include <yoc/fota.h>
  5. #if CONFIG_FOTA_USE_HTTPC == 1
  6. #include <yoc/netio.h>
  7. #include <ulog/ulog.h>
  8. #include <aos/kernel.h>
  9. #include <errno.h>
  10. #include <http_client.h>
  11. #include "util/network.h"
  12. #define TAG "fota-httpc"
  13. typedef struct {
  14. http_client_handle_t http_client;
  15. const char *cert;
  16. const char *path;
  17. } httpc_priv_t;
  18. static int _http_event_handler(http_client_event_t *evt)
  19. {
  20. switch(evt->event_id) {
  21. case HTTP_EVENT_ERROR:
  22. LOGD(TAG, "HTTP_EVENT_ERROR");
  23. break;
  24. case HTTP_EVENT_ON_CONNECTED:
  25. LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  26. break;
  27. case HTTP_EVENT_HEADER_SENT:
  28. LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  29. break;
  30. case HTTP_EVENT_ON_HEADER:
  31. // LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
  32. break;
  33. case HTTP_EVENT_ON_DATA:
  34. // LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
  35. break;
  36. case HTTP_EVENT_ON_FINISH:
  37. LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  38. break;
  39. case HTTP_EVENT_DISCONNECTED:
  40. LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
  41. break;
  42. }
  43. return 0;
  44. }
  45. static bool process_again(int status_code)
  46. {
  47. switch (status_code) {
  48. case HttpStatus_MovedPermanently:
  49. case HttpStatus_Found:
  50. case HttpStatus_TemporaryRedirect:
  51. case HttpStatus_Unauthorized:
  52. return true;
  53. default:
  54. return false;
  55. }
  56. return false;
  57. }
  58. static http_errors_t _http_handle_response_code(http_client_handle_t http_client, int status_code, char *buffer, int buf_size, int data_size)
  59. {
  60. http_errors_t err;
  61. if (status_code == HttpStatus_MovedPermanently || status_code == HttpStatus_Found || status_code == HttpStatus_TemporaryRedirect) {
  62. err = http_client_set_redirection(http_client);
  63. if (err != HTTP_CLI_OK) {
  64. LOGE(TAG, "URL redirection Failed");
  65. return err;
  66. }
  67. } else if (status_code == HttpStatus_Unauthorized) {
  68. return HTTP_CLI_FAIL;
  69. } else if(status_code == HttpStatus_NotFound || status_code == HttpStatus_Forbidden) {
  70. LOGE(TAG, "File not found(%d)", status_code);
  71. return HTTP_CLI_FAIL;
  72. } else if (status_code == HttpStatus_InternalError) {
  73. LOGE(TAG, "Server error occurred(%d)", status_code);
  74. return HTTP_CLI_FAIL;
  75. }
  76. // process_again() returns true only in case of redirection.
  77. if (data_size > 0 && process_again(status_code)) {
  78. /*
  79. * In case of redirection, http_client_read() is called
  80. * to clear the response buffer of http_client.
  81. */
  82. int data_read;
  83. while (data_size > buf_size) {
  84. data_read = http_client_read(http_client, buffer, buf_size);
  85. if (data_read <= 0) {
  86. return HTTP_CLI_OK;
  87. }
  88. data_size -= buf_size;
  89. }
  90. data_read = http_client_read(http_client, buffer, data_size);
  91. if (data_read <= 0) {
  92. return HTTP_CLI_OK;
  93. }
  94. }
  95. return HTTP_CLI_OK;
  96. }
  97. static http_errors_t _http_connect(http_client_handle_t http_client, char *buffer, int buf_size)
  98. {
  99. #define MAX_REDIRECTION_COUNT 10
  100. http_errors_t err = HTTP_CLI_FAIL;
  101. int status_code = 0, header_ret;
  102. int redirect_counter = 0;
  103. do {
  104. if (redirect_counter++ > MAX_REDIRECTION_COUNT) {
  105. LOGE(TAG, "redirect_counter is max");
  106. return HTTP_CLI_FAIL;
  107. }
  108. if (process_again(status_code)) {
  109. LOGD(TAG, "process again,status code:%d", status_code);
  110. }
  111. err = http_client_open(http_client, 0);
  112. if (err != HTTP_CLI_OK) {
  113. LOGE(TAG, "Failed to open HTTP connection");
  114. return err;
  115. }
  116. header_ret = http_client_fetch_headers(http_client);
  117. if (header_ret < 0) {
  118. LOGE(TAG, "header_ret:%d", header_ret);
  119. return header_ret;
  120. }
  121. LOGD(TAG, "header_ret:%d", header_ret);
  122. status_code = http_client_get_status_code(http_client);
  123. LOGD(TAG, "status code:%d", status_code);
  124. err = _http_handle_response_code(http_client, status_code, buffer, buf_size, header_ret);
  125. if (err != HTTP_CLI_OK) {
  126. LOGE(TAG, "e handle resp code:%d", err);
  127. return err;
  128. }
  129. } while (process_again(status_code));
  130. return err;
  131. }
  132. static void _http_cleanup(http_client_handle_t client)
  133. {
  134. LOGD(TAG, "httpc cleanup...");
  135. if (client) {
  136. http_client_cleanup(client);
  137. }
  138. }
  139. static int http_read(netio_t *io, uint8_t *buffer, int length, int timeoutms)
  140. {
  141. #define RANGE_BUF_SIZE 56
  142. int read_len;
  143. long long time1ms;
  144. httpc_priv_t *priv = (httpc_priv_t *)io->private;
  145. http_client_handle_t client = priv->http_client;
  146. if (client == NULL) {
  147. int ret = 0;
  148. int statuscode;
  149. char *range = NULL;
  150. char *buffer = NULL;
  151. http_errors_t err;
  152. http_client_config_t config = {0};
  153. config.method = HTTP_METHOD_GET;
  154. config.url = priv->path;
  155. config.timeout_ms = timeoutms;
  156. config.buffer_size = BUFFER_SIZE;
  157. config.cert_pem = priv->cert;
  158. config.event_handler = _http_event_handler;
  159. client = http_client_init(&config);
  160. if (!client) {
  161. LOGE(TAG, "Client init e");
  162. ret = -1;
  163. goto exit;
  164. }
  165. LOGD(TAG, "http client init ok.[%s]", config.url);
  166. LOGD(TAG, "http read connecting........");
  167. buffer = aos_zalloc(BUFFER_SIZE + 1);
  168. if (!buffer) {
  169. LOGE(TAG, "http open nomem.");
  170. ret = -ENOMEM;
  171. goto exit;
  172. }
  173. range = (void *)aos_zalloc(RANGE_BUF_SIZE);
  174. if (!range) {
  175. LOGE(TAG, "range malloc e");
  176. ret = -ENOMEM;
  177. goto exit;
  178. }
  179. snprintf(range, RANGE_BUF_SIZE, "bytes=%d-", io->offset);
  180. LOGD(TAG, "range:%s", range);
  181. err = http_client_set_header(client, "Range", range);
  182. if (err != HTTP_CLI_OK) {
  183. LOGE(TAG, "set header Range e");
  184. ret = -1;
  185. goto exit;
  186. }
  187. err = http_client_set_header(client, "Connection", "keep-alive");
  188. if (err != HTTP_CLI_OK) {
  189. LOGE(TAG, "set header Connection e");
  190. ret = -1;
  191. goto exit;
  192. }
  193. err = http_client_set_header(client, "Cache-Control", "no-cache");
  194. if (err != HTTP_CLI_OK) {
  195. LOGE(TAG, "set header Cache-Control e");
  196. ret = -1;
  197. goto exit;
  198. }
  199. err = _http_connect(client, buffer, BUFFER_SIZE);
  200. if (err != HTTP_CLI_OK) {
  201. LOGE(TAG, "Client connect e");
  202. ret = -1;
  203. goto exit;
  204. }
  205. statuscode = http_client_get_status_code(client);
  206. if (statuscode != 206) {
  207. LOGE(TAG, "not 206 Partial Content");
  208. ret = -1;
  209. goto exit;
  210. }
  211. io->size = http_client_get_content_length(client);
  212. io->size += io->offset;
  213. LOGD(TAG, "range_len: %d", io->size);
  214. priv->http_client = client;
  215. exit:
  216. if (buffer) aos_free(buffer);
  217. if (range) aos_free(range);
  218. if (ret != 0) {
  219. _http_cleanup(client);
  220. return ret;
  221. }
  222. }
  223. if (io->offset >= io->size) {
  224. LOGW(TAG, "http_read done: offset:%d tsize:%d", io->offset, io->size);
  225. return 0;
  226. }
  227. time1ms = aos_now_ms();
  228. read_len = 0;
  229. while (read_len < length) {
  230. if (aos_now_ms() - time1ms > timeoutms) {
  231. break;
  232. }
  233. int data_read = http_client_read(client, (char *)buffer + read_len, length - read_len);
  234. if (data_read < 0) {
  235. LOGW(TAG, "http client read error:%d, errno:%d", data_read, errno);
  236. return data_read;
  237. } else if (data_read == 0) {
  238. LOGD(TAG, "http client read 0 size");
  239. break;
  240. }
  241. read_len += data_read;
  242. }
  243. io->offset += read_len;
  244. return read_len;
  245. }
  246. static int http_open(netio_t *io, const char *path)
  247. {
  248. const char *cert;
  249. LOGD(TAG, "http open:%s", path);
  250. cert = (const char *)io->cls->private;
  251. io->size = 0;
  252. io->offset = 0;
  253. io->block_size = CONFIG_FOTA_BUFFER_SIZE;
  254. httpc_priv_t *priv = aos_zalloc_check(sizeof(httpc_priv_t));
  255. if (!priv) {
  256. LOGE(TAG, "http open nomem e");
  257. return -1;
  258. }
  259. priv->http_client = NULL;
  260. priv->path = path;
  261. priv->cert = cert;
  262. io->private = priv;
  263. #if 0 // just for test, mem leak...
  264. char *tempbuffer = (char *)aos_malloc(400);
  265. if (tempbuffer == NULL) {
  266. LOGE(TAG, "malloc tempbuffer failed");
  267. return -1;
  268. }
  269. memset(tempbuffer, 0, 400);
  270. if (strstr(priv->path, "https://")) {
  271. snprintf(tempbuffer, 400, "http://%s", priv->path + strlen("https://"));
  272. }
  273. priv->path = tempbuffer;
  274. #endif
  275. #if 0 //just for test redirection
  276. priv->path = "http://192.168.1.102:8889";
  277. #endif
  278. return 0;
  279. }
  280. static int http_seek(netio_t *io, size_t offset, int whence)
  281. {
  282. io->offset = offset;
  283. return 0;
  284. }
  285. static int http_close(netio_t *io)
  286. {
  287. httpc_priv_t *priv = (httpc_priv_t *)io->private;
  288. http_client_handle_t client = priv->http_client;
  289. _http_cleanup(client);
  290. aos_free(priv);
  291. return 0;
  292. }
  293. netio_cls_t httpc_cls = {
  294. .name = "http",
  295. .read = http_read,
  296. .seek = http_seek,
  297. .open = http_open,
  298. .close = http_close,
  299. };
  300. int netio_register_httpc(const char *cert)
  301. {
  302. httpc_cls.private = (void *)cert;
  303. return netio_register(&httpc_cls);
  304. }
  305. #endif