1 /*
2  * Copyright (C) 2015-2019 Alibaba Group Holding Limited
3  */
4 
5 #include <stdarg.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include "aos_httpc.h"
11 #include "aos_network.h"
12 #include "aos_socket.h"
13 #include "aos_system.h"
14 #include "aos_tcp.h"
15 #include "httpclient.h"
16 #include "py/builtin.h"
17 #include "py/mperrno.h"
18 #include "py/obj.h"
19 #include "py/runtime.h"
20 #include "ulog/ulog.h"
21 // #include "amp_task.h"
22 // #include "be_inl.h"
23 // #include "http.h"
24 #include "cJSON.h"
25 // #include "amp_list.h"
26 
27 #define LOG_TAG                     "MOD-HTTP"
28 
29 #define MOD_STR                     "HTTP"
30 #define HTTP_BUFF_SIZE              (12 * 1024)
31 #define HTTP_HEADER_SIZE            2048
32 #define HTTP_HEADER_COUNT           8
33 #define HTTP_REQUEST_PARAMS_LEN_MAX 2048
34 #define HTTP_SEND_RECV_TIMEOUT      10000
35 #define HTTP_REQUEST_TIMEOUT        30000
36 #define HTTP_DEFAULT_HEADER_NAME    "content-type"
37 #define HTTP_DEFAULT_HEADER_DATA    "application/json"
38 #define HTTP_CRLF                   "\r\n"
39 
40 #define ADDON_TSK_PRIORRITY         (AOS_DEFAULT_APP_PRI + 3)
41 static int http_header_index = 0;
42 typedef struct {
43     char *name;
44     char *data;
45 } http_header_t;
46 
47 typedef struct {
48     char *url;
49     char *filepath;
50     int method;
51     http_header_t http_header[HTTP_HEADER_COUNT];
52     uint32_t timeout;
53     char *params;
54     char *rec_data_buffer;
55     char *rec_header_buffer;
56     int params_len;
57     mp_obj_t cb;
58 } http_param_t;
59 
60 extern const mp_obj_type_t http_client_type;
61 
strncasestr(const char * str,const char * key)62 static char *strncasestr(const char *str, const char *key)
63 {
64     int len;
65 
66     if (!str || !key)
67         return NULL;
68 
69     len = strlen(key);
70     if (len == 0)
71         return NULL;
72 
73     while (*str) {
74         if (!strncasecmp(str, key, len))
75             return str;
76         ++str;
77     }
78     return NULL;
79 }
80 
parse_url(const char * url,char * uri)81 static void parse_url(const char *url, char *uri)
82 {
83     char url_dup[1024] = { 0 };
84 
85     if (url == NULL) {
86         LOGD(LOG_TAG, "url is null");
87         return;
88     }
89     memcpy(url_dup, url, strlen(url));
90     char *start = NULL;
91     char *p_slash = NULL;
92 
93 #if 1
94     const char *protocol = "https";
95 #else
96     const char *protocol = "http";
97 #endif
98 
99     if (strncmp(url_dup, protocol, strlen(protocol)) == 0) {
100         start = url_dup + strlen(protocol) + 3;
101         p_slash = strchr(start, '/');
102         if (p_slash != NULL) {
103             memcpy(uri, p_slash, strlen(p_slash));
104             *p_slash = '\0';
105         } else {
106             memcpy(uri, '/', 1);
107         }
108     }
109 }
110 
pyamp_httpc_construct_header(char * buf,uint16_t buf_size,const char * name,const char * data)111 int32_t pyamp_httpc_construct_header(char *buf, uint16_t buf_size,
112                                      const char *name, const char *data)
113 {
114     uint32_t hdr_len;
115     uint32_t hdr_data_len;
116     int32_t hdr_length;
117 
118     if (buf == NULL || buf_size == 0 || name == NULL || data == NULL) {
119         return HTTP_EARG;
120     }
121 
122     hdr_len = strlen(name);
123     hdr_data_len = strlen(data);
124     hdr_length = hdr_len + hdr_data_len + 4;
125 
126     if (hdr_length < 0 || hdr_length > buf_size) {
127         return HTTP_ENOBUFS;
128     }
129 
130     memcpy(buf, name, hdr_len);
131     buf += hdr_len;
132     memcpy(buf, ": ", 2);
133     buf += 2;
134     memcpy(buf, data, hdr_data_len);
135     buf += hdr_data_len;
136     memcpy(buf, HTTP_CRLF, 2);
137 
138     return hdr_length;
139 }
140 
http_request_notify(void * pdata)141 static void http_request_notify(void *pdata)
142 {
143     mp_obj_t dict = MP_OBJ_NULL;
144 
145     http_param_t *msg = (http_param_t *)pdata;
146     LOGD(LOG_TAG, "buf is %s \r\n", msg->rec_data_buffer);
147     LOGD(LOG_TAG, "buf len is %d \r\n", strlen(msg->rec_data_buffer));
148     LOGD(LOG_TAG, "header is %s \r\n", msg->rec_header_buffer);
149     LOGD(LOG_TAG, "buf len is %d \r\n", strlen(msg->rec_header_buffer));
150 
151     dict = mp_obj_new_dict(2);
152     mp_obj_dict_store(MP_OBJ_FROM_PTR(dict),
153                         mp_obj_new_str("header", 6),
154                         mp_obj_new_str(msg->rec_header_buffer, strlen(msg->rec_header_buffer)));
155     mp_obj_dict_store(MP_OBJ_FROM_PTR(dict),
156                         mp_obj_new_str("body", 4),
157                         mp_obj_new_str(msg->rec_data_buffer, strlen(msg->rec_data_buffer)));
158 
159     if (msg->cb != mp_const_none) {
160         callback_to_python(msg->cb, dict);
161     }
162 }
163 
http_download_notify(void * pdata)164 static void http_download_notify(void *pdata)
165 {
166     http_param_t *msg = (http_param_t *)pdata;
167     LOGD(LOG_TAG, "buf is %s \r\n", msg->rec_data_buffer);
168     LOGD(LOG_TAG, "buf len is %d \r\n", strlen(msg->rec_data_buffer));
169 
170     if (msg->cb != mp_const_none) {
171         callback_to_python(msg->cb,
172                            mp_obj_new_str(msg->rec_data_buffer, strlen(msg->rec_data_buffer)));
173     }
174 }
175 
176 static char customer_header[HTTP_HEADER_SIZE] = { 0 };
177 static char rsp_header[HTTP_HEADER_SIZE] = { 0 };
178 static char rsp_buf[HTTP_BUFF_SIZE];
179 static char req_buf[HTTP_BUFF_SIZE];
180 
181 /* create task for http download */
task_http_download_func(void * arg)182 static void task_http_download_func(void *arg)
183 {
184     httpclient_t client = { 0 };
185     httpclient_data_t client_data = { 0 };
186     http_param_t *http_param = (http_param_t *)arg;
187     int num = 0;
188     int ret, i = 0;
189     bool result = false;
190     char *header = "Accept: */*\r\n";
191 
192     char *req_url = http_param->url;
193 
194     if (!aos_access(http_param->filepath, 0)) {
195         aos_remove(http_param->filepath);
196     }
197 
198     int fd = aos_open(http_param->filepath, O_CREAT | O_RDWR | O_APPEND);
199     if (fd < 0) {
200         printf("aos open fd %d fail\n", fd);
201         result = false;
202         goto end;
203     }
204     memset(rsp_buf, 0, sizeof(rsp_buf));
205     client_data.response_buf = rsp_buf;
206     client_data.response_buf_len = sizeof(rsp_buf);
207     client_data.header_buf = rsp_header;
208     client_data.header_buf_len = HTTP_HEADER_SIZE;
209 
210     httpclient_set_custom_header(&client, header);
211 
212     ret = httpclient_conn(&client, req_url);
213 
214     if (!ret) {
215         ret = httpclient_send(&client, req_url, HTTP_GET, &client_data);
216 
217         do {
218             ret = httpclient_recv(&client, &client_data);
219             // printf("response_content_len=%d,
220             // retrieve_len=%d,content_block_len=%d\n",
221             // client_data.response_content_len,client_data.retrieve_len,client_data.content_block_len);
222             // printf("ismore=%d\n", client_data.is_more);
223 
224             num = aos_write(fd, client_data.response_buf,
225                             client_data.content_block_len);
226             if (num > 0) {
227                 // printf("aos_write num=%d\n", num);
228             } else {
229                 result = false;
230                 break;
231             }
232         } while (client_data.is_more);
233         if (client_data.response_content_len == 0)
234             result = false;
235         else
236             result = true;
237     } else {
238         result = false;
239         printf("[%s]httpclient_conn fail, ret : %d\n", __func__, ret);
240     }
241 
242     aos_close(fd);
243     httpclient_clse(&client);
244 
245 end:
246     if (result) {
247         if (http_param->rec_data_buffer)
248             strcpy(http_param->rec_data_buffer, "success");
249     } else {
250         if (http_param->rec_data_buffer)
251             strcpy(http_param->rec_data_buffer, "fail");
252     }
253 
254     http_download_notify(http_param);
255 
256     if (http_param && http_param->rec_data_buffer) {
257         aos_free(http_param->rec_data_buffer);
258     }
259 
260     if (http_param && http_param->rec_header_buffer) {
261         aos_free(http_param->rec_header_buffer);
262     }
263 
264     if (http_param) {
265         aos_free(http_param);
266     }
267 
268     aos_task_exit(0);
269 }
270 
271 /* create task for http request */
task_http_request_func(void * arg)272 static void task_http_request_func(void *arg)
273 {
274     char *url = NULL;
275     uint32_t timeout = 0;
276     int http_method = 0;
277     int i = 0;
278     int ret = 0;
279     httpclient_t client = { 0 };
280     httpclient_data_t client_data = { 0 };
281     http_param_t *http_param = (http_param_t *)arg;
282 
283     url = http_param->url;
284     timeout = http_param->timeout;
285     http_method = http_param->method;
286 
287     LOGD(LOG_TAG, "task_http_request_func url: %s", url);
288     LOGD(LOG_TAG, "task_http_request_func method: %d", http_method);
289     LOGD(LOG_TAG, "task_http_request_func timeout: %d", timeout);
290 
291     memset(rsp_buf, 0, HTTP_BUFF_SIZE);
292     client_data.response_buf = rsp_buf;
293     client_data.response_buf_len = sizeof(rsp_buf);
294     client_data.header_buf = rsp_header;
295     client_data.header_buf_len = HTTP_HEADER_SIZE;
296     aos_msleep(50); /* need do things after state changed in main task */
297 
298     for (i = 0; i < http_header_index; i++) {
299         pyamp_httpc_construct_header(customer_header, HTTP_HEADER_SIZE,
300                                      http_param->http_header[i].name,
301                                      http_param->http_header[i].data);
302     }
303     http_header_index = 0;
304     httpclient_set_custom_header(&client, customer_header);
305 
306     if (http_method == HTTP_GET) {
307         LOGD(LOG_TAG, "http GET request=%s,timeout=%d\r\n", url, timeout);
308         ret = httpclient_get(&client, url, &client_data);
309         if (ret >= 0) {
310             LOGD(LOG_TAG, "GET Data received: %s, len=%d \r\n",
311                  client_data.response_buf, client_data.response_buf_len);
312             strcpy(http_param->rec_data_buffer, client_data.response_buf);
313             strcpy(http_param->rec_header_buffer, client_data.header_buf);
314         }
315     } else if (http_method == HTTP_HEAD) {
316         LOGD(LOG_TAG, "http HEAD request=%s,timeout=%d\r\n", url, timeout);
317         ret = httpclient_head(&client, url, &client_data);
318         if (ret >= 0) {
319             LOGD(LOG_TAG, "HEAD Data received: %s, len=%d \r\n",
320                  client_data.response_buf, client_data.response_buf_len);
321             strcpy(http_param->rec_data_buffer, client_data.response_buf);
322             strcpy(http_param->rec_header_buffer, client_data.header_buf);
323         }
324     } else if (http_method == HTTP_POST) {
325         LOGD(LOG_TAG, "http POST request=%s,post_buf=%s,timeout=%d\r\n", url,
326              http_param->params, timeout);
327         memset(req_buf, 0, sizeof(req_buf));
328         strcpy(req_buf, http_param->params);
329         client_data.post_buf = req_buf;
330         client_data.post_buf_len = sizeof(req_buf);
331         client_data.post_content_type = "application/x-www-form-urlencoded";
332         ret = httpclient_post(&client, url, &client_data);
333         if (ret >= 0) {
334             LOGD(LOG_TAG, "POST Data received: %s, len=%d \r\n",
335                  client_data.response_buf, client_data.response_buf_len);
336             strcpy(http_param->rec_data_buffer, client_data.response_buf);
337             strcpy(http_param->rec_header_buffer, client_data.header_buf);
338         }
339     } else if (http_method == HTTP_PUT) {
340         LOGD(LOG_TAG, "http PUT request=%s,data=%s,timeout=%d\r\n", url,
341              http_param->params, timeout);
342         memset(req_buf, 0, sizeof(req_buf));
343         strcpy(req_buf, http_param->params);
344         client_data.post_buf = req_buf;
345         client_data.post_buf_len = sizeof(req_buf);
346         client_data.post_content_type = "application/x-www-form-urlencoded";
347         ret = httpclient_put(&client, url, &client_data);
348         if (ret >= 0) {
349             LOGD(LOG_TAG, "Data received: %s, len=%d \r\n",
350                  client_data.response_buf, client_data.response_buf_len);
351             strcpy(http_param->rec_data_buffer, client_data.response_buf);
352             strcpy(http_param->rec_header_buffer, client_data.header_buf);
353         }
354     } else if (http_method == HTTP_DELETE) {
355         LOGD(LOG_TAG, "http DELETE request=%s,data=%s,timeout=%d\r\n", url,
356              http_param->params, timeout);
357         memset(req_buf, 0, sizeof(req_buf));
358         strcpy(req_buf, http_param->params);
359         client_data.post_buf = req_buf;
360         client_data.post_buf_len = sizeof(req_buf);
361         client_data.post_content_type = "application/x-www-form-urlencoded";
362         ret = httpclient_put(&client, url, &client_data);
363         if (ret >= 0) {
364             LOGD(LOG_TAG, "Data received: %s, len=%d \r\n",
365                  client_data.response_buf, client_data.response_buf_len);
366             strcpy(http_param->rec_data_buffer, client_data.response_buf);
367             strcpy(http_param->rec_header_buffer, client_data.header_buf);
368         }
369     } else {
370         ret = httpclient_get(&client, url, &client_data);
371         if (ret >= 0) {
372             LOGD(LOG_TAG, "Data received: %s, len=%d \r\n",
373                  client_data.response_buf, client_data.response_buf_len);
374             strcpy(http_param->rec_data_buffer, client_data.response_buf);
375             strcpy(http_param->rec_header_buffer, client_data.header_buf);
376         }
377     }
378 
379     httpclient_clse(&client);
380     http_request_notify(http_param);
381 
382     if (http_param && http_param->rec_data_buffer) {
383         aos_free(http_param->rec_data_buffer);
384     }
385 
386     if (http_param && http_param->rec_header_buffer) {
387         aos_free(http_param->rec_header_buffer);
388     }
389 
390     if (http_param) {
391         aos_free(http_param);
392     }
393 
394     LOGD(LOG_TAG, " task_http_request_func end \r\n ");
395     aos_task_exit(0);
396 
397     return;
398 }
399 
http_download(mp_obj_t data,mp_obj_t callback)400 STATIC mp_obj_t http_download(mp_obj_t data, mp_obj_t callback)
401 {
402     http_param_t *http_param = NULL;
403     char *http_buffer = NULL;
404     char *http_header_buffer = NULL;
405     const char *url = NULL;
406     const char *filepath = NULL;
407 
408     aos_task_t http_task;
409 
410     http_param = (http_param_t *)aos_malloc(sizeof(http_param_t));
411     if (!http_param) {
412         LOGD(LOG_TAG, "allocate memory failed\n");
413         goto done;
414     }
415     memset(http_param, 0, sizeof(http_param_t));
416 
417     http_buffer = aos_malloc(32 + 1);
418     if (!http_buffer) {
419         LOGE(LOG_TAG, "allocate memory failed\n");
420         goto done;
421     }
422     memset(http_buffer, 0, 32 + 1);
423     http_param->rec_data_buffer = http_buffer;
424 
425     http_header_buffer = aos_malloc(HTTP_HEADER_SIZE + 1);
426     if (!http_header_buffer) {
427         LOGE(LOG_TAG, "allocate memory failed\n");
428         goto done;
429     }
430     memset(http_header_buffer, 0, HTTP_HEADER_SIZE + 1);
431     http_param->rec_header_buffer = http_header_buffer;
432 
433     /* get http download url */
434 
435     mp_obj_t index = mp_obj_new_str_via_qstr("url", 3);
436 
437     url = mp_obj_str_get_str(mp_obj_dict_get(data, index));
438     http_param->url = url;
439 
440     /* get http download filepath */
441 
442     index = mp_obj_new_str_via_qstr("filepath", 8);
443 
444     filepath = mp_obj_str_get_str(mp_obj_dict_get(data, index));
445     http_param->filepath = filepath;
446 
447     /* callback */
448     http_param->cb = callback;
449 
450     aos_task_new_ext(&http_task, "amp http download task",
451                      task_http_download_func, http_param, 1024 * 8,
452                      ADDON_TSK_PRIORRITY);
453 
454     return mp_obj_new_int(0);
455 
456 done:
457     if (http_buffer) {
458         aos_free(http_buffer);
459     }
460 
461     if (http_header_buffer) {
462         aos_free(http_header_buffer);
463     }
464 
465     if (http_param) {
466         aos_free(http_param);
467     }
468 
469     return mp_const_none;
470 }
471 
472 MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_http_download, http_download);
473 
http_request(mp_obj_t data,mp_obj_t callback)474 static mp_obj_t http_request(mp_obj_t data, mp_obj_t callback)
475 {
476     http_param_t *http_param = NULL;
477     char *http_buffer = NULL;
478     char *http_header_buffer = NULL;
479     const char *method = NULL;
480     const char *url = NULL;
481     cJSON *param_root;
482     int http_method = 0;
483     int timeout = 0;
484     char localip[32];
485     int i;
486     aos_task_t http_task;
487 
488     if (!mp_obj_is_dict_or_ordereddict(data)) {
489         LOGE(LOG_TAG, "%s param type error, param type must be dict\r\n",
490              __func__);
491         return mp_const_none;
492     }
493 
494     http_param = (http_param_t *)aos_malloc(sizeof(http_param_t));
495     if (!http_param) {
496         LOGE(LOG_TAG, "allocate memory failed\n");
497         goto done;
498     }
499     memset(http_param, 0, sizeof(http_param_t));
500 
501     http_buffer = aos_malloc(HTTP_BUFF_SIZE + 1);
502     if (!http_buffer) {
503         LOGE(LOG_TAG, "allocate memory failed\n");
504         goto done;
505     }
506     memset(http_buffer, 0, HTTP_BUFF_SIZE + 1);
507     http_param->rec_data_buffer = http_buffer;
508 
509     http_header_buffer = aos_malloc(HTTP_HEADER_SIZE + 1);
510     if (!http_header_buffer) {
511         LOGE(LOG_TAG, "allocate memory failed\n");
512         goto done;
513     }
514     memset(http_header_buffer, 0, HTTP_HEADER_SIZE + 1);
515     http_param->rec_header_buffer = http_header_buffer;
516 
517     /* get http request url */
518     mp_obj_t index = mp_obj_new_str_via_qstr("url", 3);
519 
520     url = mp_obj_str_get_str(mp_obj_dict_get(data, index));
521     http_param->url = url;
522 
523     /* get http request method */
524     index = mp_obj_new_str_via_qstr("method", 6);
525 
526     method = mp_obj_str_get_str(mp_obj_dict_get(data, index));
527     if (strcmp(method, "GET") == 0) {
528         http_method = HTTP_GET; /* GET */
529     } else if (strcmp(method, "HEAD") == 0) {
530         http_method = HTTP_HEAD; /* HEAD */
531     } else if (strcmp(method, "POST") == 0) {
532         http_method = HTTP_POST; /* POST */
533     } else if (strcmp(method, "PUT") == 0) {
534         http_method = HTTP_PUT; /* PUT */
535     } else if (strcmp(method, "DELETE") == 0) {
536         http_method = HTTP_DELETE; /* DELETE */
537     } else {
538         http_method = HTTP_GET;
539     }
540     http_param->method = http_method;
541 
542     /* get http request timeout */
543     index = mp_obj_new_str_via_qstr("timeout", 7);
544     timeout = mp_obj_get_int(mp_obj_dict_get(data, index));
545     http_param->timeout = timeout;
546 
547     LOGD(LOG_TAG, "timeout is : %d", http_param->timeout);
548 
549     /* get http request headers */
550     index = mp_obj_new_str_via_qstr("headers", 7);
551     mp_obj_t header = mp_obj_dict_get(data, index);
552     if (header == MP_OBJ_NULL) {
553         // pass
554         http_param->http_header[0].name = HTTP_DEFAULT_HEADER_NAME;
555         http_param->http_header[0].data = HTTP_DEFAULT_HEADER_DATA;
556         http_header_index++;
557     } else if (mp_obj_is_type(header, &mp_type_dict)) {
558         // dictionary
559         mp_map_t *map = mp_obj_dict_get_map(header);
560         // assert(args2_len + 2 * map->used <= args2_alloc); // should have
561         // enough, since kw_dict_len is in this case hinted correctly above
562         for (size_t i = 0; i < map->alloc; i++) {
563             if (mp_map_slot_is_filled(map, i)) {
564                 // the key must be a qstr, so intern it if it's a string
565                 mp_obj_t key = map->table[i].key;
566                 if (!mp_obj_is_qstr(key)) {
567                     key = mp_obj_str_intern_checked(key);
568                 }
569                 http_header_index = i;
570                 http_param->http_header[http_header_index].name =
571                     mp_obj_str_get_str(map->table[i].key);
572                 http_param->http_header[http_header_index].data =
573                     mp_obj_str_get_str(map->table[i].value);
574             }
575         }
576     }
577 
578     index = mp_obj_new_str_via_qstr("params", 6);
579     mp_obj_t params = mp_obj_dict_get(data, index);
580 
581     http_param->params = mp_obj_str_get_str(params);
582     http_param->params_len = strlen(http_param->params);
583 
584     LOGD(LOG_TAG, "url: %s", http_param->url);
585     LOGD(LOG_TAG, "method: %d", http_param->method);
586     LOGD(LOG_TAG, "timeout: %d", http_param->timeout);
587     for (i = 0; i < http_header_index; i++) {
588         LOGD(LOG_TAG, MOD_STR, "headers: %s:%s",
589              http_param->http_header[i].name, http_param->http_header[i].data);
590     }
591 
592     /* callback */
593 
594     http_param->cb = callback;
595 
596     aos_task_new_ext(&http_task, "amp http task", task_http_request_func,
597                      http_param, 1024 * 8, ADDON_TSK_PRIORRITY);
598 
599     return mp_obj_new_int(0);
600 
601 done:
602 
603     if (http_buffer) {
604         aos_free(http_buffer);
605     }
606 
607     if (http_header_buffer) {
608         aos_free(http_header_buffer);
609     }
610 
611     if (http_param) {
612         aos_free(http_param);
613     }
614 
615     return mp_const_none;
616 }
617 MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_http_request, http_request);
618 
619 STATIC const mp_rom_map_elem_t http_locals_dict_table[] = {
620     { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_http) },
621     { MP_OBJ_NEW_QSTR(MP_QSTR_request), MP_ROM_PTR(&mp_obj_http_request) },
622     { MP_OBJ_NEW_QSTR(MP_QSTR_download), MP_ROM_PTR(&mp_obj_http_download) },
623     { MP_OBJ_NEW_QSTR(MP_QSTR_client), MP_ROM_PTR(&http_client_type) },
624 };
625 
626 STATIC MP_DEFINE_CONST_DICT(http_locals_dict, http_locals_dict_table);
627 
628 const mp_obj_module_t http_module = {
629     .base = { &mp_type_module },
630     .globals = (mp_obj_dict_t *)&http_locals_dict,
631 };
632