1 /*
2  * Copyright (C) 2015-2018 Alibaba Group Holding Limited
3  */
4 #include "linkkit/infra/infra_config.h"
5 
6 #ifdef INFRA_HTTPC
7 
8 #include <string.h>
9 #include <stddef.h>
10 #include <stdlib.h>
11 
12 #include "linkkit/infra/infra_types.h"
13 #include "linkkit/infra/infra_defs.h"
14 #include "linkkit/infra/infra_httpc.h"
15 #include "linkkit/infra/infra_net.h"
16 #include "linkkit/infra/infra_timer.h"
17 #include "linkkit/wrappers/wrappers.h"
18 
19 #ifdef INFRA_LOG
20 #include "linkkit/infra/infra_log.h"
21 #define httpc_err(...)   log_err("httpc", __VA_ARGS__)
22 #define httpc_info(...)  log_info("httpc", __VA_ARGS__)
23 #define httpc_debug(...) log_debug("httpc", __VA_ARGS__)
24 #else
25 #define httpc_err(...)
26 #define httpc_info(...)
27 #define httpc_debug(...)
28 #endif
29 
30 #define HTTPCLIENT_MIN(x, y)      (((x) < (y)) ? (x) : (y))
31 #define HTTPCLIENT_MAX(x, y)      (((x) > (y)) ? (x) : (y))
32 
33 #define HTTPCLIENT_READ_BUF_SIZE  (1024) /* read payload */
34 #define HTTPCLIENT_RAED_HEAD_SIZE (32)   /* read header */
35 #define HTTPCLIENT_SEND_BUF_SIZE  (1024) /* send */
36 
37 #define HTTPCLIENT_MAX_URL_LEN    (256)
38 
39 #define HTTP_RETRIEVE_MORE_DATA   (1) /**< More data needs to be retrieved. */
40 
41 #if defined(MBEDTLS_DEBUG_C)
42 #define DEBUG_LEVEL 2
43 #endif
44 #define HTTPCLIENT_CHUNK_SIZE (1024)
45 
46 static int _utils_parse_url(const char *url, char *host, char *path);
47 static int _http_recv(httpclient_t *client, char *buf, int max_len,
48                       int *p_read_len, uint32_t timeout);
49 static int _http_get_response_body(httpclient_t *client, char *data, int len,
50                                    uint32_t timeout,
51                                    httpclient_data_t *client_data);
52 static int _http_parse_response_header(httpclient_t *client, char *data,
53                                        int len, uint32_t timeout,
54                                        httpclient_data_t *client_data);
55 
_utils_parse_url(const char * url,char * host,char * path)56 static int _utils_parse_url(const char *url, char *host, char *path)
57 {
58     char *host_ptr = (char *)strstr(url, "://");
59     uint32_t host_len = 0;
60     uint32_t path_len;
61     /* char *port_ptr; */
62     char *path_ptr;
63     char *fragment_ptr;
64 
65     if (host_ptr == NULL) {
66         return -1; /* URL is invalid */
67     }
68     host_ptr += 3;
69 
70     path_ptr = strchr(host_ptr, '/');
71     if (NULL == path_ptr) {
72         return -2;
73     }
74 
75     if (host_len == 0) {
76         host_len = path_ptr - host_ptr;
77     }
78 
79     memcpy(host, host_ptr, host_len);
80     host[host_len] = '\0';
81     fragment_ptr = strchr(host_ptr, '#');
82     if (fragment_ptr != NULL) {
83         path_len = fragment_ptr - path_ptr;
84     } else {
85         path_len = strlen(path_ptr);
86     }
87 
88     memcpy(path, path_ptr, path_len);
89     path[path_len] = '\0';
90 
91     return SUCCESS_RETURN;
92 }
93 
_utils_fill_tx_buffer(httpclient_t * client,char * send_buf,int * send_idx,char * buf,uint32_t len)94 static int _utils_fill_tx_buffer(
95     httpclient_t *client, char *send_buf, int *send_idx, char *buf,
96     uint32_t len) /* 0 on success, err code on failure */
97 {
98     int ret;
99     int cp_len;
100     int idx = *send_idx;
101 
102     if (len == 0) {
103         len = strlen(buf);
104     }
105     do {
106         if ((HTTPCLIENT_SEND_BUF_SIZE - idx) >= len) {
107             cp_len = len;
108         } else {
109             cp_len = HTTPCLIENT_SEND_BUF_SIZE - idx;
110         }
111 
112         memcpy(send_buf + idx, buf, cp_len);
113         idx += cp_len;
114         len -= cp_len;
115 
116         if (idx == HTTPCLIENT_SEND_BUF_SIZE) {
117             ret = client->net.write(&client->net, send_buf,
118                                     HTTPCLIENT_SEND_BUF_SIZE, 5000);
119             if (ret) {
120                 return ret;
121             }
122         }
123     } while (len);
124 
125     *send_idx = idx;
126     return SUCCESS_RETURN;
127 }
128 
_http_send_header(httpclient_t * client,const char * host,const char * path,int method,httpclient_data_t * client_data)129 static int _http_send_header(httpclient_t *client, const char *host,
130                              const char *path, int method,
131                              httpclient_data_t *client_data)
132 {
133     int len;
134     char send_buf[HTTPCLIENT_SEND_BUF_SIZE] = { 0 };
135     char buf[HTTPCLIENT_SEND_BUF_SIZE] = { 0 };
136     char *meth = (method == HTTPCLIENT_GET)      ? "GET"
137                  : (method == HTTPCLIENT_POST)   ? "POST"
138                  : (method == HTTPCLIENT_PUT)    ? "PUT"
139                  : (method == HTTPCLIENT_DELETE) ? "DELETE"
140                  : (method == HTTPCLIENT_HEAD)   ? "HEAD"
141                                                  : "";
142     int ret;
143 
144     /* Send request */
145     memset(send_buf, 0, HTTPCLIENT_SEND_BUF_SIZE);
146     len = 0; /* Reset send buffer */
147 
148     HAL_Snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path,
149                  host); /* Write request */
150 
151     ret = _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
152     if (ret) {
153         /* httpc_err("Could not write request"); */
154         return ERROR_HTTP_CONN;
155     }
156 
157     /* Add user header information */
158     if (client->header) {
159         _utils_fill_tx_buffer(client, send_buf, &len, (char *)client->header,
160                               strlen(client->header));
161     }
162 
163     if (client_data->post_buf != NULL) {
164         HAL_Snprintf(buf, sizeof(buf), "Content-Length: %d\r\n",
165                      client_data->post_buf_len);
166         _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
167 
168         if (client_data->post_content_type != NULL) {
169             HAL_Snprintf(buf, sizeof(buf), "Content-Type: %s\r\n",
170                          client_data->post_content_type);
171             _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
172         }
173     }
174 
175     /* Close headers */
176     _utils_fill_tx_buffer(client, send_buf, &len, "\r\n", 0);
177 
178 #ifdef INFRA_LOG
179     log_multi_line(LOG_DEBUG_LEVEL, "REQUEST", "%s", send_buf, ">");
180 #endif
181 
182     /* ret = httpclient_tcp_send_all(client->net.handle, send_buf, len); */
183     ret = client->net.write(&client->net, send_buf, len, 5000);
184     if (ret <= 0) {
185         httpc_err("ret = client->net.write() = %d", ret);
186         return (ret == 0) ? ERROR_HTTP_CLOSED : ERROR_HTTP_CONN;
187     }
188 
189     return SUCCESS_RETURN;
190 }
191 
_http_send_userdata(httpclient_t * client,httpclient_data_t * client_data)192 int _http_send_userdata(httpclient_t *client, httpclient_data_t *client_data)
193 {
194     int ret = 0;
195 
196     if (client_data->post_buf && client_data->post_buf_len) {
197         /* ret = httpclient_tcp_send_all(client->handle, (char
198          * *)client_data->post_buf, client_data->post_buf_len); */
199         ret = client->net.write(&client->net, (char *)client_data->post_buf,
200                                 client_data->post_buf_len, 5000);
201         httpc_debug("client_data->post_buf: %s, ret is %d",
202                     client_data->post_buf, ret);
203         if (ret <= 0) {
204             return (ret == 0)
205                        ? ERROR_HTTP_CLOSED
206                        : ERROR_HTTP_CONN; /* Connection was closed by server */
207         }
208     }
209 
210     return SUCCESS_RETURN;
211 }
212 
213 /* 0 on success, err code on failure */
_http_recv(httpclient_t * client,char * buf,int max_len,int * p_read_len,uint32_t timeout_ms)214 static int _http_recv(httpclient_t *client, char *buf, int max_len,
215                       int *p_read_len, uint32_t timeout_ms)
216 {
217     int ret = 0;
218     iotx_time_t timer;
219 
220     iotx_time_init(&timer);
221     utils_time_countdown_ms(&timer, timeout_ms);
222 
223     *p_read_len = 0;
224 
225     ret = client->net.read(&client->net, buf, max_len, iotx_time_left(&timer));
226     /* httpc_debug("Recv: | %s", buf); */
227     httpc_info("ret of _http_recv is %d", ret);
228 
229     if (ret > 0) {
230         *p_read_len = ret;
231         return 0;
232     } else if (ret == 0) {
233         /* timeout */
234         return FAIL_RETURN;
235     } else {
236         return ERROR_HTTP_CONN;
237     }
238 }
239 
240 #define MIN_TIMEOUT     (100)
241 #define MAX_RETRY_COUNT (600)
242 
_utils_check_deadloop(int len,iotx_time_t * timer,int ret,unsigned int * dead_loop_count,unsigned int * extend_count)243 static int _utils_check_deadloop(int len, iotx_time_t *timer, int ret,
244                                  unsigned int *dead_loop_count,
245                                  unsigned int *extend_count)
246 {
247     /* if timeout reduce to zero, it will be translated into NULL for select
248      * function in TLS lib */
249     /* it would lead to indenfinite behavior, so we avoid it */
250     if (iotx_time_left(timer) < MIN_TIMEOUT) {
251         (*extend_count)++;
252         utils_time_countdown_ms(timer, MIN_TIMEOUT);
253     }
254 
255     /* if it falls into deadloop before reconnected to internet, we just quit*/
256     if ((0 == len) && (0 == iotx_time_left(timer)) && (FAIL_RETURN == ret)) {
257         (*dead_loop_count)++;
258         if (*dead_loop_count > MAX_RETRY_COUNT) {
259             httpc_err("deadloop detected, exit");
260             return ERROR_HTTP_CONN;
261         }
262     } else {
263         *dead_loop_count = 0;
264     }
265 
266     /*if the internet connection is fixed during the loop, the download stream
267      * might be disconnected. we have to quit */
268     if ((0 == len) && (*extend_count > 2 * MAX_RETRY_COUNT) &&
269         (FAIL_RETURN == ret)) {
270         httpc_err("extend timer for too many times, exit");
271         return ERROR_HTTP_CONN;
272     }
273     return SUCCESS_RETURN;
274 }
275 
_utils_fill_rx_buf(int * recv_count,int len_to_write_to_respons_buf,httpclient_data_t * client_data,char * data)276 static int _utils_fill_rx_buf(int *recv_count, int len_to_write_to_respons_buf,
277                               httpclient_data_t *client_data, char *data)
278 {
279     int count = *recv_count;
280     if (count + len_to_write_to_respons_buf <
281         client_data->response_buf_len - 1) {
282         memcpy(client_data->response_buf + count, data,
283                len_to_write_to_respons_buf);
284         count += len_to_write_to_respons_buf;
285         client_data->response_buf[count] = '\0';
286         client_data->retrieve_len -= len_to_write_to_respons_buf;
287         *recv_count = count;
288         return SUCCESS_RETURN;
289     } else {
290         memcpy(client_data->response_buf + count, data,
291                client_data->response_buf_len - 1 - count);
292         client_data->response_buf[client_data->response_buf_len - 1] = '\0';
293         client_data->retrieve_len -=
294             (client_data->response_buf_len - 1 - count);
295         return HTTP_RETRIEVE_MORE_DATA;
296     }
297 }
298 
_http_get_response_body(httpclient_t * client,char * data,int data_len_actually_received,uint32_t timeout_ms,httpclient_data_t * client_data)299 static int _http_get_response_body(httpclient_t *client, char *data,
300                                    int data_len_actually_received,
301                                    uint32_t timeout_ms,
302                                    httpclient_data_t *client_data)
303 {
304     int written_response_buf_len = 0;
305     int len_to_write_to_respons_buf = 0;
306     iotx_time_t timer;
307 
308     iotx_time_init(&timer);
309     utils_time_countdown_ms(&timer, timeout_ms);
310 
311     /* Receive data */
312     /* httpc_debug("Current data: %s", data); */
313 
314     client_data->is_more = IOT_TRUE;
315 
316     /* the header is not received finished */
317     if (client_data->response_content_len == -1 &&
318         client_data->is_chunked == IOT_FALSE) {
319         /* can not enter this if */
320         /* TODO check the way to go into this branch */
321         httpc_err("header is not received yet");
322         return ERROR_HTTP_CONN;
323     }
324 
325     while (1) {
326         unsigned int dead_loop_count = 0;
327         unsigned int extend_count = 0;
328         do {
329             int res;
330             /* move previous fetched data into response_buf */
331             len_to_write_to_respons_buf = HTTPCLIENT_MIN(
332                 data_len_actually_received, client_data->retrieve_len);
333             res = _utils_fill_rx_buf(&written_response_buf_len,
334                                      len_to_write_to_respons_buf, client_data,
335                                      data);
336             if (HTTP_RETRIEVE_MORE_DATA == res) {
337                 return HTTP_RETRIEVE_MORE_DATA;
338             }
339 
340             /* get data from internet and put into "data" buf temporary */
341             if (client_data->retrieve_len) {
342                 int ret;
343                 int max_len_to_receive =
344                     HTTPCLIENT_MIN(HTTPCLIENT_CHUNK_SIZE - 1,
345                                    client_data->response_buf_len - 1 -
346                                        written_response_buf_len);
347                 max_len_to_receive = HTTPCLIENT_MIN(max_len_to_receive,
348                                                     client_data->retrieve_len);
349 
350                 ret = _http_recv(client, data, max_len_to_receive,
351                                  &data_len_actually_received,
352                                  iotx_time_left(&timer));
353                 if (ret == ERROR_HTTP_CONN) {
354                     return ret;
355                 }
356                 httpc_debug(
357                     "Total- remaind Payload: %d Bytes; currently Read: %d "
358                     "Bytes",
359                     client_data->retrieve_len, data_len_actually_received);
360 
361                 /* TODO  add deadloop processing*/
362                 ret =
363                     _utils_check_deadloop(data_len_actually_received, &timer,
364                                           ret, &dead_loop_count, &extend_count);
365                 if (ERROR_HTTP_CONN == ret) {
366                     return ret;
367                 }
368             }
369         } while (client_data->retrieve_len);
370         client_data->is_more = IOT_FALSE;
371         break;
372     }
373 
374     return SUCCESS_RETURN;
375 }
376 
_http_parse_response_header(httpclient_t * client,char * data,int len,uint32_t timeout_ms,httpclient_data_t * client_data)377 static int _http_parse_response_header(httpclient_t *client, char *data,
378                                        int len, uint32_t timeout_ms,
379                                        httpclient_data_t *client_data)
380 {
381     int crlf_pos;
382     iotx_time_t timer;
383     char *tmp_ptr, *ptr_body_end;
384     int new_trf_len, ret;
385     char *crlf_ptr;
386 
387     iotx_time_init(&timer);
388     utils_time_countdown_ms(&timer, timeout_ms);
389 
390     client_data->response_content_len = -1;
391 
392     /* http client response */
393     /* <status-line> HTTP/1.1 200 OK(CRLF)
394 
395        <headers> ...(CRLF)
396 
397        <blank line> (CRLF)
398 
399       [<response-body>] */
400     crlf_ptr = strstr(data, "\r\n");
401     if (crlf_ptr == NULL) {
402         httpc_err("\r\n not found");
403         return ERROR_HTTP_UNRESOLVED_DNS;
404     }
405 
406     crlf_pos = crlf_ptr - data;
407     data[crlf_pos] = '\0';
408     client->response_code = atoi(data + 9);
409     httpc_debug("Reading headers: %s", data);
410     memmove(data, &data[crlf_pos + 2],
411             len - (crlf_pos + 2) +
412                 1);        /* Be sure to move NULL-terminating char as well */
413     len -= (crlf_pos + 2); /* remove status_line length */
414     client_data->is_chunked = IOT_FALSE;
415 
416     /*If not ending of response body*/
417     /* try to read more header again until find response head ending "\r\n\r\n"
418      */
419     while (NULL == (ptr_body_end = strstr(data, "\r\n\r\n"))) {
420         /* try to read more header */
421         int max_remain_len = HTTPCLIENT_READ_BUF_SIZE - len - 1;
422         if (max_remain_len <= 0) {
423             httpc_debug("buffer exceeded max\n");
424             return ERROR_HTTP_PARSE;
425         }
426         max_remain_len = max_remain_len > HTTPCLIENT_RAED_HEAD_SIZE
427                              ? HTTPCLIENT_RAED_HEAD_SIZE
428                              : max_remain_len;
429         ret = _http_recv(client, data + len, max_remain_len, &new_trf_len,
430                          iotx_time_left(&timer));
431 
432         if (ret == ERROR_HTTP_CONN) {
433             return ret;
434         }
435         len += new_trf_len;
436         data[len] = '\0';
437     }
438 
439     /* parse response_content_len */
440     tmp_ptr = strstr(data, "Content-Length");
441     if (tmp_ptr != NULL) {
442         client_data->response_content_len =
443             atoi(tmp_ptr + strlen("Content-Length: "));
444         client_data->retrieve_len = client_data->response_content_len;
445     } else {
446         httpc_err("Could not parse header");
447         return ERROR_HTTP;
448     }
449 
450     /* remove header length */
451     /* len is Had read body's length */
452     /* if client_data->response_content_len != 0, it is know response length */
453     /* the remain length is client_data->response_content_len - len */
454     len = len - (ptr_body_end + 4 - data);
455     memmove(data, ptr_body_end + 4, len + 1);
456     client_data->response_received_len += len;
457     return _http_get_response_body(client, data, len, iotx_time_left(&timer),
458                                    client_data);
459 }
460 
httpclient_connect(httpclient_t * client)461 int httpclient_connect(httpclient_t *client)
462 {
463     int retry_max = 3;
464     int retry_cnt = 1;
465     int retry_interval = 1000;
466     int rc = -1;
467 
468     do {
469         client->net.handle = 0;
470         httpc_debug("calling TCP or TLS connect HAL for [%d/%d] iteration",
471                     retry_cnt, retry_max);
472 
473         rc = client->net.connect(&client->net);
474         if (0 != rc) {
475             client->net.disconnect(&client->net);
476             httpc_err("TCP or TLS connect failed, rc = %d", rc);
477             HAL_SleepMs(retry_interval);
478             continue;
479         } else {
480             httpc_debug(
481                 "rc = client->net.connect() = %d, success @ [%d/%d] iteration",
482                 rc, retry_cnt, retry_max);
483             break;
484         }
485     } while (++retry_cnt <= retry_max);
486 
487     return SUCCESS_RETURN;
488 }
489 
_http_send_request(httpclient_t * client,const char * host,const char * path,HTTPCLIENT_REQUEST_TYPE method,httpclient_data_t * client_data)490 int _http_send_request(httpclient_t *client, const char *host, const char *path,
491                        HTTPCLIENT_REQUEST_TYPE method,
492                        httpclient_data_t *client_data)
493 {
494     int ret = ERROR_HTTP_CONN;
495 
496     if (0 == client->net.handle) {
497         return -1;
498     }
499 
500     ret = _http_send_header(client, host, path, method, client_data);
501     if (ret != 0) {
502         return ret;
503     }
504 
505     if (method == HTTPCLIENT_POST || method == HTTPCLIENT_PUT) {
506         ret = _http_send_userdata(client, client_data);
507         if (ret < 0) {
508             ret = ret;
509         }
510     }
511 
512     return ret;
513 }
514 
httpclient_recv_response(httpclient_t * client,uint32_t timeout_ms,httpclient_data_t * client_data)515 int httpclient_recv_response(httpclient_t *client, uint32_t timeout_ms,
516                              httpclient_data_t *client_data)
517 {
518     int reclen = 0, ret = ERROR_HTTP_CONN;
519     char buf[HTTPCLIENT_READ_BUF_SIZE] = { 0 };
520     iotx_time_t timer;
521 
522     iotx_time_init(&timer);
523     utils_time_countdown_ms(&timer, timeout_ms);
524 
525     if (0 == client->net.handle) {
526         httpc_err("not connection have been established");
527         return ret;
528     }
529 
530     if (client_data->is_more) {
531         client_data->response_buf[0] = '\0';
532         ret = _http_get_response_body(client, buf, reclen,
533                                       iotx_time_left(&timer), client_data);
534     } else {
535         client_data->is_more = 1;
536         /* try to read header */
537         ret = _http_recv(client, buf, HTTPCLIENT_RAED_HEAD_SIZE, &reclen,
538                          iotx_time_left(&timer));
539         if (ret != 0) {
540             return ret;
541         }
542 
543         buf[reclen] = '\0';
544 
545         if (reclen) {
546 #ifdef INFRA_LOG
547             log_multi_line(LOG_DEBUG_LEVEL, "RESPONSE", "%s", buf, "<");
548 #endif
549             ret = _http_parse_response_header(
550                 client, buf, reclen, iotx_time_left(&timer), client_data);
551         }
552     }
553 
554     return ret;
555 }
556 
httpclient_close(httpclient_t * client)557 void httpclient_close(httpclient_t *client)
558 {
559     if (client->net.handle > 0) {
560         client->net.disconnect(&client->net);
561     }
562     client->net.handle = 0;
563     httpc_info("client disconnected");
564 }
565 
_http_send(httpclient_t * client,const char * url,int port,const char * ca_crt,HTTPCLIENT_REQUEST_TYPE method,httpclient_data_t * client_data)566 static int _http_send(httpclient_t *client, const char *url, int port,
567                       const char *ca_crt, HTTPCLIENT_REQUEST_TYPE method,
568                       httpclient_data_t *client_data)
569 {
570     int ret;
571     char host[HTTPCLIENT_MAX_URL_LEN] = { 0 };
572     char path[HTTPCLIENT_MAX_URL_LEN] = { 0 };
573 
574     /* First we need to parse the url (http[s]://host[:port][/[path]]) */
575     ret = _utils_parse_url(url, host, path);
576     if (ret != SUCCESS_RETURN) {
577         httpc_err("_utils_parse_url fail returned %d", ret);
578         return ret;
579     }
580 
581     if (0 == client->net.handle) {
582         /* Establish connection if no. */
583         ret = iotx_net_init(&client->net, host, port, ca_crt);
584         if (0 != ret) {
585             return ret;
586         }
587 
588         ret = httpclient_connect(client);
589         if (0 != ret) {
590             httpclient_close(client);
591             return ret;
592         }
593 
594         ret = _http_send_request(client, host, path, method, client_data);
595         if (0 != ret) {
596             httpc_err("_http_send_request is error, ret = %d", ret);
597             return ret;
598         }
599     }
600     return SUCCESS_RETURN;
601 }
602 
httpclient_common(httpclient_t * client,const char * url,int port,const char * ca_crt,HTTPCLIENT_REQUEST_TYPE method,uint32_t timeout_ms,httpclient_data_t * client_data)603 int httpclient_common(httpclient_t *client, const char *url, int port,
604                       const char *ca_crt, HTTPCLIENT_REQUEST_TYPE method,
605                       uint32_t timeout_ms, httpclient_data_t *client_data)
606 {
607     iotx_time_t timer;
608     int ret = _http_send(client, url, port, ca_crt, method, client_data);
609     if (SUCCESS_RETURN != ret) {
610         httpclient_close(client);
611         return ret;
612     }
613 
614     iotx_time_init(&timer);
615     utils_time_countdown_ms(&timer, timeout_ms);
616 
617     if ((NULL != client_data->response_buf) &&
618         (0 != client_data->response_buf_len)) {
619         ret = httpclient_recv_response(client, iotx_time_left(&timer),
620                                        client_data);
621         if (ret < 0) {
622             httpc_err("httpclient_recv_response is error,ret = %d", ret);
623             httpclient_close(client);
624             return ret;
625         }
626     }
627 
628     if (!client_data->is_more) {
629         /* Close the HTTP if no more data. */
630         httpc_info("close http channel");
631         httpclient_close(client);
632     }
633 
634     ret = 0;
635     return ret;
636 }
637 
iotx_post(httpclient_t * client,const char * url,int port,const char * ca_crt,httpclient_data_t * client_data)638 int iotx_post(httpclient_t *client, const char *url, int port,
639               const char *ca_crt, httpclient_data_t *client_data)
640 {
641     return _http_send(client, url, port, ca_crt, HTTPCLIENT_POST, client_data);
642 }
643 
644 /**********************************************************************************/
645 
646 typedef struct {
647     httpclient_t client;
648     httpclient_data_t http_client_data;
649     int receive_maxlen;
650     int already_received;
651     char *url;
652     int port;
653     iotx_http_method_t method;
654     char *header;
655     char *cert;
656     int timeout;
657     recvcallback recv_cb;
658     void *recv_ctx;
659 } wrapper_http_handle_t;
660 
_replase_str(char ** ptr,void * data)661 static int _replase_str(char **ptr, void *data)
662 {
663     if (*ptr != NULL) {
664         HAL_Free(*ptr);
665     }
666     *ptr = HAL_Malloc(strlen(data) + 1);
667     if (*ptr == NULL) {
668         return FAIL_RETURN;
669     }
670     memset(*ptr, 0, strlen(data) + 1);
671     memcpy(*ptr, data, strlen(data));
672 
673     return SUCCESS_RETURN;
674 }
675 
wrapper_http_init(void)676 void *wrapper_http_init(void)
677 {
678     wrapper_http_handle_t *handle = HAL_Malloc(sizeof(wrapper_http_handle_t));
679     if (handle == NULL) {
680         return NULL;
681     }
682     memset(handle, 0, sizeof(wrapper_http_handle_t));
683 
684     return handle;
685 }
686 
wrapper_http_setopt(void * handle,iotx_http_option_t option,void * data)687 int wrapper_http_setopt(void *handle, iotx_http_option_t option, void *data)
688 {
689     wrapper_http_handle_t *http_handle = (wrapper_http_handle_t *)handle;
690 
691     if (handle == NULL || data == NULL) {
692         return FAIL_RETURN;
693     }
694 
695     switch (option) {
696     case IOTX_HTTPOPT_URL:
697         {
698             _replase_str(&http_handle->url, data);
699         }
700         break;
701     case IOTX_HTTPOPT_PORT:
702         {
703             if (*(int *)(data) < 0) {
704                 return FAIL_RETURN;
705             }
706             http_handle->port = *(int *)(data);
707         }
708         break;
709     case IOTX_HTTPOPT_METHOD:
710         {
711             http_handle->method = *(iotx_http_method_t *)data;
712         }
713         break;
714     case IOTX_HTTPOPT_HEADER:
715         {
716             _replase_str(&http_handle->header, data);
717         }
718         break;
719     case IOTX_HTTPOPT_CERT:
720         {
721             http_handle->cert = data;
722         }
723         break;
724     case IOTX_HTTPOPT_TIMEOUT:
725         {
726             if (*(int *)(data) < 0) {
727                 return FAIL_RETURN;
728             }
729             http_handle->timeout = *(int *)(data);
730         }
731         break;
732     case IOTX_HTTPOPT_RECVCALLBACK:
733         {
734             http_handle->recv_cb = data;
735         }
736         break;
737     case IOTX_HTTPOPT_RECVMAXLEN:
738         {
739             http_handle->receive_maxlen = *(int *)(data);
740         }
741         break;
742     case IOTX_HTTPOPT_RECVCONTEXT:
743         {
744             http_handle->recv_ctx = (recvcallback)data;
745         }
746         break;
747     default:
748         {
749             httpc_err("Unknown Option");
750             return FAIL_RETURN;
751         }
752     }
753 
754     return SUCCESS_RETURN;
755 }
756 
wrapper_http_perform(void * handle,void * data,int length)757 int wrapper_http_perform(void *handle, void *data, int length)
758 {
759     int res = 0, received_this_time = 0;
760     iotx_time_t timer;
761     wrapper_http_handle_t *http_handle = (wrapper_http_handle_t *)handle;
762     char *response_payload = NULL;
763 
764     if (handle == NULL) {
765         return FAIL_RETURN;
766     }
767 
768     if ((http_handle->method == IOTX_HTTP_POST) &&
769         (data == NULL || length <= 0)) {
770         return FAIL_RETURN;
771     }
772 
773     if (http_handle->receive_maxlen <= 0) {
774         return FAIL_RETURN;
775     }
776 
777     response_payload = HAL_Malloc(http_handle->receive_maxlen);
778     if (response_payload == NULL) {
779         return FAIL_RETURN;
780     }
781     memset(response_payload, 0, http_handle->receive_maxlen);
782 
783     http_handle->client.header = http_handle->header;
784     http_handle->http_client_data.post_buf = data;
785     http_handle->http_client_data.post_buf_len = length;
786     http_handle->http_client_data.response_buf = response_payload;
787     http_handle->http_client_data.response_buf_len =
788         http_handle->receive_maxlen;
789 
790     res = _http_send(&http_handle->client, http_handle->url, http_handle->port,
791                      http_handle->cert,
792                      (HTTPCLIENT_REQUEST_TYPE)http_handle->method,
793                      &http_handle->http_client_data);
794     if (SUCCESS_RETURN != res) {
795         HAL_Free(response_payload);
796         return res;
797     }
798 
799     iotx_time_init(&timer);
800     utils_time_countdown_ms(&timer, http_handle->timeout);
801 
802     res = httpclient_recv_response(&http_handle->client, iotx_time_left(&timer),
803                                    &http_handle->http_client_data);
804     if (res < 0) {
805         httpc_err("httpclient_recv_response is error,res = %d", res);
806         HAL_Free(response_payload);
807         return res;
808     }
809 
810     received_this_time = http_handle->http_client_data.response_content_len -
811                          http_handle->http_client_data.retrieve_len -
812                          http_handle->already_received;
813     http_handle->already_received =
814         http_handle->http_client_data.response_content_len -
815         http_handle->http_client_data.retrieve_len;
816 
817     if (res >= SUCCESS_RETURN) {
818         if (http_handle->recv_cb) {
819             http_handle->recv_cb(
820                 response_payload, received_this_time,
821                 http_handle->http_client_data.response_content_len,
822                 http_handle->recv_ctx);
823         }
824     }
825 
826     HAL_Free(response_payload);
827 
828     return received_this_time;
829 }
830 
wrapper_http_deinit(void ** handle)831 void wrapper_http_deinit(void **handle)
832 {
833     wrapper_http_handle_t **http_handle = (wrapper_http_handle_t **)handle;
834 
835     if (handle == NULL || *handle == NULL) {
836         return;
837     }
838 
839     if ((*http_handle)->url) {
840         HAL_Free((*http_handle)->url);
841     }
842 
843     if ((*http_handle)->header) {
844         HAL_Free((*http_handle)->header);
845     }
846 
847     httpclient_close(&(*http_handle)->client);
848 
849     HAL_Free(*http_handle);
850     *handle = NULL;
851 }
852 
853 /**********************************************************************************/
854 
855 #endif
856