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