1 /*
2  * Copyright (C) 2015-2018 Alibaba Group Holding Limited
3  */
4 
5 #include "linkkit/infra/infra_config.h"
6 #ifdef FS_ENABLED
7 
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 
13 #include "linkkit/infra/infra_defs.h"
14 #include "linkkit/infra/infra_types.h"
15 #include "linkkit/infra/infra_httpc.h"
16 #include "linkkit/infra/infra_sha1.h"
17 #include "linkkit/infra/infra_timer.h"
18 #include "linkkit/infra/infra_list.h"
19 #include "linkkit/infra/infra_log.h"
20 
21 #include "http2_internal.h"
22 #include "linkkit/http2_api.h"
23 #include "linkkit/http2_upload_api.h"
24 #include "http2_config.h"
25 #include "linkkit/wrappers/wrappers.h"
26 
27 #define PACKET_LEN          16384
28 #define HTTP2_FS_SERVICE_ID "c/iot/sys/thing/file/upload"
29 
30 typedef enum {
31     FS_STATUS_WAITING,
32     FS_STATUS_UPLOADING,
33     FS_STATUS_BREAK,
34     FS_STATUS_END,
35     FS_STATUS_NUM
36 } file_stream_status_t;
37 
38 typedef enum {
39     FS_TYPE_NORMAL,
40     FS_TYPE_CONTINUE,
41     FS_TYPE_OVERRIDE,
42 } file_stream_type_t;
43 
44 typedef struct {
45     int idx;
46     const char *file_path;
47     char upload_id[40];
48     uint32_t spec_len;
49     uint32_t file_offset;
50     uint32_t part_len;
51     file_stream_type_t type;
52     http2_upload_id_received_cb_t opened_cb;
53     http2_upload_completed_cb_t end_cb;
54     void *user_data;
55 
56     uint8_t if_stop;
57 
58     http2_list_t list;
59 } http2_file_stream_t;
60 
61 typedef struct {
62     stream_handle_t *http2_handle;
63     const char *service_id;
64     http2_list_t file_list;
65     void *list_mutex;
66     void *file_thread;
67     int upload_idx;
68 } http2_file_stream_ctx_t;
69 
70 typedef struct {
71     char *send_buffer;
72     const char *upload_id;
73     uint32_t file_offset;
74     uint32_t part_len;
75 } fs_send_ext_info_t;
76 
77 static http2_file_stream_ctx_t g_http2_fs_ctx = { 0 };
78 static http2_stream_cb_t callback_func = { 0 };
79 
80 /* utils, get file sizef */
http2_stream_get_file_size(const char * file_name)81 static int http2_stream_get_file_size(const char *file_name)
82 {
83     void *fp = NULL;
84     int size = 0;
85 
86     fp = HAL_Fopen(file_name, "r");
87     if (fp == NULL) {
88         h2_err("The file %s can not be opened.\n", file_name);
89         return -1;
90     }
91     HAL_Fseek(fp, 0L, HAL_SEEK_END);
92     size = HAL_Ftell(fp);
93     HAL_Fclose(fp);
94     return size;
95 }
96 
97 /* utils, get file name */
_http2_fs_get_filename(const char * file_path)98 static const char *_http2_fs_get_filename(const char *file_path)
99 {
100     const char *p_name = NULL;
101 
102     if (file_path == NULL) {
103         return NULL;
104     }
105 
106     p_name = file_path + strlen(file_path);
107 
108     while (--p_name != file_path) {
109         if (*p_name == '/') {
110             p_name++;
111             break;
112         }
113     }
114 
115     return p_name;
116 }
117 
118 /* utils, read file data */
http2_stream_get_file_data(const char * file_name,char * data,int len,int offset)119 static int http2_stream_get_file_data(const char *file_name, char *data,
120                                       int len, int offset)
121 {
122     void *fp = NULL;
123     int ret = 0;
124 
125     fp = HAL_Fopen(file_name, "r");
126     if (fp == NULL) {
127         h2_err("The file %s can not be opened.\n", file_name);
128         return -1;
129     }
130     ret = HAL_Fseek(fp, offset, HAL_SEEK_SET);
131     if (ret != 0) {
132         HAL_Fclose(fp);
133         h2_err("The file %s can not move offset.\n", file_name);
134         return -1;
135     }
136     ret = HAL_Fread(data, len, 1, fp);
137     HAL_Fclose(fp);
138     return len;
139 }
140 
141 /* open http2 file upload channel */
_http2_fs_open_channel(http2_file_stream_t * fs_node,stream_data_info_t * info)142 static int _http2_fs_open_channel(http2_file_stream_t *fs_node,
143                                   stream_data_info_t *info)
144 {
145     stream_handle_t *h2_handle = g_http2_fs_ctx.http2_handle;
146     header_ext_info_t ext_header;
147     fs_rsp_header_val_t *open_rsp = (fs_rsp_header_val_t *)info->user_data;
148     const char *filename = NULL;
149     int ret = UPLOAD_ERROR_COMMON;
150 
151     /* header for normal upload */
152     http2_header header_filename[] = {
153         MAKE_HEADER_CS("x-file-name", ""),
154     };
155 
156     /* header for override operation */
157     http2_header header_overwrite[] = {
158         MAKE_HEADER_CS("x-file-name", ""),
159         MAKE_HEADER_CS("x-file-overwrite", "1"),
160     };
161 
162     /* header for continue operation */
163     http2_header header_uploadid[] = {
164         MAKE_HEADER("x-file-upload-id", ""),
165     };
166 
167     filename = _http2_fs_get_filename(fs_node->file_path);
168     h2_info("filename = %s", filename);
169 
170     header_filename[0].value = (char *)filename;
171     header_filename[0].valuelen = strlen(filename);
172     header_overwrite[0].value = (char *)filename;
173     header_overwrite[0].valuelen = strlen(filename);
174 
175     /* setup http2 ext_header */
176     switch (fs_node->type) {
177     case FS_TYPE_NORMAL:
178         {
179             ext_header.num = 1;
180             ext_header.nva = header_filename;
181         }
182         break;
183     case FS_TYPE_OVERRIDE:
184         {
185             ext_header.num = 2;
186             ext_header.nva = header_overwrite;
187         }
188         break;
189     case FS_TYPE_CONTINUE:
190         {
191             /* upload id must be exist */
192             header_uploadid[0].value = fs_node->upload_id;
193             header_uploadid[0].valuelen = strlen(fs_node->upload_id);
194             ext_header.num = 1;
195             ext_header.nva = header_uploadid;
196         }
197         break;
198     default:
199         break;
200     }
201 
202     ret = IOT_HTTP2_Stream_Open(h2_handle, info, &ext_header);
203     if (ret < 0 || open_rsp->fs_offset == -1 ||
204         open_rsp->fs_upload_id[0] == '\0') {
205         h2_err("IOT_HTTP2_Stream_Open failed %d\n", ret);
206         return UPLOAD_STREAM_OPEN_FAILED;
207     }
208     h2_info("fs channel open succeed");
209     return SUCCESS_RETURN;
210 }
211 
212 /* file part data send sync api */
_http2_fs_part_send_sync(http2_file_stream_t * fs_node,stream_data_info_t * info,fs_send_ext_info_t * ext_info)213 static int _http2_fs_part_send_sync(http2_file_stream_t *fs_node,
214                                     stream_data_info_t *info,
215                                     fs_send_ext_info_t *ext_info)
216 {
217     stream_handle_t *h2_handle = g_http2_fs_ctx.http2_handle;
218     header_ext_info_t ext_header;
219     int res;
220     http2_header header_uploadid[] = {
221         MAKE_HEADER_CS("x-file-upload-id", ext_info->upload_id),
222     };
223 
224     /* setup ext header */
225     ext_header.num = 1;
226     ext_header.nva = header_uploadid;
227 
228     /* setup Stream_Send params */
229     info->stream = ext_info->send_buffer;
230     info->stream_len = ext_info->part_len;
231     info->send_len = 0;
232     info->packet_len = FS_UPLOAD_PACKET_LEN;
233 
234     while (info->send_len < info->stream_len) {
235         if (!h2_handle->init_state) {
236             res = UPLOAD_ERROR_COMMON;
237             break;
238         }
239 
240         res = http2_stream_get_file_data(
241             fs_node->file_path, ext_info->send_buffer, FS_UPLOAD_PACKET_LEN,
242             (info->send_len + ext_info->file_offset)); /* offset used */
243         if (res <= 0) {
244             res = UPLOAD_FILE_READ_FAILED;
245             break;
246         }
247         info->packet_len = res;
248 
249         /* adjust the packet len */
250         if (info->stream_len - info->send_len < info->packet_len) {
251             info->packet_len = info->stream_len - info->send_len;
252         }
253 
254         res = IOT_HTTP2_Stream_Send(h2_handle, info, &ext_header);
255         if (res < 0) {
256             res = UPLOAD_STREAM_SEND_FAILED;
257             break;
258         }
259         h2_debug("send len = %d\n", info->send_len);
260 
261         if (fs_node->if_stop) {
262             res = UPLOAD_STOP_BY_IOCTL;
263             break;
264         }
265 
266         res = SUCCESS_RETURN;
267     }
268 
269     return res;
270 }
271 
_http2_fs_node_handle(http2_file_stream_t * fs_node)272 void *_http2_fs_node_handle(http2_file_stream_t *fs_node)
273 {
274     stream_handle_t *h2_handle = g_http2_fs_ctx.http2_handle;
275     int filesize = 0;
276     int upload_len = 0;
277     fs_rsp_header_val_t rsp_data;
278     fs_send_ext_info_t send_ext_info;
279     stream_data_info_t channel_info;
280     uint32_t part_len = 0;
281     int res = FAIL_RETURN;
282 
283     /* params check */
284     if (h2_handle == NULL) {
285         /* TODO: handle */
286         return NULL;
287     }
288 
289     /* get fileszie */
290     filesize = http2_stream_get_file_size(fs_node->file_path);
291     if (filesize <= 0) {
292         if (fs_node->end_cb) {
293             fs_node->end_cb(fs_node->file_path, UPLOAD_FILE_NOT_EXIST,
294                             fs_node->user_data);
295         }
296 
297         return NULL;
298     }
299     h2_info("filesize = %d", filesize);
300 
301     /* open http2 file upload channel */
302     memset(&rsp_data, 0, sizeof(fs_rsp_header_val_t));
303     memset(&channel_info, 0, sizeof(stream_data_info_t));
304     channel_info.identify = g_http2_fs_ctx.service_id;
305     channel_info.user_data = (void *)&rsp_data;
306 
307     res = _http2_fs_open_channel(fs_node, &channel_info);
308     if (res < SUCCESS_RETURN) {
309         if (fs_node->end_cb) {
310             fs_node->end_cb(fs_node->file_path, res, fs_node->user_data);
311         }
312 
313         return NULL;
314     }
315 
316     h2_info("upload_id = %s", rsp_data.fs_upload_id);
317     h2_info("upload_offset = %d", rsp_data.fs_offset);
318     if (fs_node->opened_cb) {
319         fs_node->opened_cb(fs_node->file_path, rsp_data.fs_upload_id,
320                            fs_node->user_data);
321     }
322 
323     if (fs_node->spec_len &&
324         (fs_node->spec_len + rsp_data.fs_offset < filesize)) {
325         upload_len = fs_node->spec_len + rsp_data.fs_offset;
326     } else {
327         upload_len = filesize;
328     }
329 
330     /* setup send part len */
331     if ((fs_node->part_len < (1024 * 100)) ||
332         (fs_node->part_len > (1024 * 1024 * 100))) {
333         part_len = FS_UPLOAD_PART_LEN;
334     } else {
335         part_len = fs_node->part_len;
336     }
337 
338     /* send http2 file upload data */
339     send_ext_info.upload_id = rsp_data.fs_upload_id;
340     send_ext_info.file_offset = rsp_data.fs_offset;
341     send_ext_info.send_buffer = HTTP2_STREAM_MALLOC(FS_UPLOAD_PACKET_LEN);
342     if (send_ext_info.send_buffer == NULL) {
343         if (fs_node->end_cb) {
344             fs_node->end_cb(fs_node->file_path, UPLOAD_MALLOC_FAILED,
345                             fs_node->user_data);
346         }
347 
348         return NULL;
349     }
350 
351     do {
352         /* setup the part len */
353         send_ext_info.part_len =
354             ((upload_len - send_ext_info.file_offset) < part_len)
355                 ? (upload_len - send_ext_info.file_offset)
356                 : part_len;
357 
358         res = _http2_fs_part_send_sync(fs_node, &channel_info, &send_ext_info);
359         if (res < SUCCESS_RETURN) {
360             h2_err("fs send return %d", res);
361             break;
362         }
363 
364         send_ext_info.file_offset += send_ext_info.part_len;
365         h2_info("file offset = %d now", send_ext_info.file_offset);
366     } while (send_ext_info.file_offset < upload_len);
367 
368     if (res < 0) {
369         if (fs_node->end_cb) {
370             fs_node->end_cb(fs_node->file_path, res, fs_node->user_data);
371         }
372 
373         HTTP2_STREAM_FREE(channel_info.channel_id);
374         HTTP2_STREAM_FREE(send_ext_info.send_buffer);
375         return NULL;
376     }
377 
378     /* close http2 file upload channel */
379     IOT_HTTP2_FS_Close(h2_handle, &channel_info, NULL);
380 
381     if (fs_node->end_cb) {
382         fs_node->end_cb(fs_node->file_path, UPLOAD_SUCCESS, fs_node->user_data);
383     }
384 
385     HTTP2_STREAM_FREE(send_ext_info.send_buffer);
386     return NULL;
387 }
388 
http_upload_file_func(void * fs_data)389 static void *http_upload_file_func(void *fs_data)
390 {
391     http2_file_stream_ctx_t *fs_ctx = (http2_file_stream_ctx_t *)fs_data;
392     http2_file_stream_t *node = NULL;
393     if (fs_ctx == NULL) {
394         return NULL;
395     }
396 
397     while (fs_ctx->http2_handle->init_state) {
398         HAL_MutexLock(fs_ctx->list_mutex);
399         if (!list_empty((list_head_t *)&g_http2_fs_ctx.file_list)) {
400             node =
401                 list_first_entry(&fs_ctx->file_list, http2_file_stream_t, list);
402             HAL_MutexUnlock(fs_ctx->list_mutex);
403 
404             /* execute upload routine */
405             _http2_fs_node_handle((void *)node);
406 
407             /* delete the completed node */
408             HAL_MutexLock(fs_ctx->list_mutex);
409             list_del((list_head_t *)&node->list);
410             HTTP2_STREAM_FREE(node);
411             HAL_MutexUnlock(fs_ctx->list_mutex);
412         } else {
413             HAL_MutexUnlock(fs_ctx->list_mutex);
414             h2_debug("file list is empty, file upload thread exit\n");
415             g_http2_fs_ctx.file_thread = NULL;
416             break;
417         }
418     }
419 
420     return NULL;
421 }
422 
_http2_fs_list_insert(http2_file_stream_ctx_t * fs_ctx,http2_file_stream_t * node)423 static void _http2_fs_list_insert(http2_file_stream_ctx_t *fs_ctx,
424                                   http2_file_stream_t *node)
425 {
426     INIT_LIST_HEAD((list_head_t *)&node->list);
427     HAL_MutexLock(fs_ctx->list_mutex);
428     list_add_tail((list_head_t *)&node->list,
429                   (list_head_t *)&fs_ctx->file_list);
430     HAL_MutexUnlock(fs_ctx->list_mutex);
431 }
432 
433 typedef enum {
434     HTTP2_IOCTL_STOP_UPLOAD,
435     HTTP2_IOCTL_COMMAND_NUM,
436 } http2_file_upload_ioctl_command_t;
437 
_http2_fs_list_search_by_idx(int idx,http2_file_stream_t ** search_node)438 static int _http2_fs_list_search_by_idx(int idx,
439                                         http2_file_stream_t **search_node)
440 {
441     http2_file_stream_t *node = NULL;
442 
443     HAL_MutexLock(g_http2_fs_ctx.list_mutex);
444 
445     list_for_each_entry(node, &g_http2_fs_ctx.file_list, list,
446                         http2_file_stream_t)
447     {
448         if (idx == node->idx) {
449             *search_node = node;
450             HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
451             return SUCCESS_RETURN;
452         }
453     }
454 
455     HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
456     *search_node = NULL;
457     return FAIL_RETURN;
458 }
459 
IOT_HTTP2_Ioctl(int upload_idx,int command,void * data)460 int IOT_HTTP2_Ioctl(int upload_idx, int command, void *data)
461 {
462     http2_file_stream_t *node = NULL;
463 
464     if (g_http2_fs_ctx.http2_handle == NULL) {
465         return UPLOAD_ERROR_COMMON;
466     }
467 
468     _http2_fs_list_search_by_idx(upload_idx, &node);
469     if (node == NULL) {
470         return UPLOAD_ERROR_COMMON;
471     }
472 
473     switch (command) {
474     case HTTP2_IOCTL_STOP_UPLOAD:
475         {
476             if (g_http2_fs_ctx.http2_handle) {
477                 HAL_MutexLock(g_http2_fs_ctx.list_mutex);
478                 node->if_stop = 1;
479                 HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
480                 return SUCCESS_RETURN;
481             } else {
482                 return UPLOAD_ERROR_COMMON;
483             }
484         }
485         break;
486     default:
487         {
488             return UPLOAD_ERROR_COMMON;
489         }
490     }
491 
492     return SUCCESS_RETURN;
493 }
494 
IOT_HTTP2_UploadFile_Connect(http2_upload_conn_info_t * conn_info,http2_status_cb_t * cb)495 void *IOT_HTTP2_UploadFile_Connect(http2_upload_conn_info_t *conn_info,
496                                    http2_status_cb_t *cb)
497 {
498     void *handle;
499 
500     memset(&callback_func, 0, sizeof(http2_stream_cb_t));
501 
502     if (cb != NULL) {
503         callback_func.on_reconnect_cb = cb->on_reconnect_cb;
504         callback_func.on_disconnect_cb = cb->on_disconnect_cb;
505     }
506 
507     handle = IOT_HTTP2_Connect((device_conn_info_t *)conn_info, &callback_func);
508     if (handle == NULL) {
509         return handle;
510     }
511 
512     /* TODO */
513     g_http2_fs_ctx.list_mutex = HAL_MutexCreate();
514     if (g_http2_fs_ctx.list_mutex == NULL) {
515         h2_err("fs mutex create error\n");
516         IOT_HTTP2_UploadFile_Disconnect(handle);
517         return NULL;
518     }
519 
520     INIT_LIST_HEAD((list_head_t *)&(g_http2_fs_ctx.file_list));
521     g_http2_fs_ctx.http2_handle = handle;
522     g_http2_fs_ctx.service_id = HTTP2_FS_SERVICE_ID;
523 
524     return handle;
525 }
526 
IOT_HTTP2_UploadFile_Request(void * http2_handle,http2_upload_params_t * params,http2_upload_result_cb_t * cb,void * user_data)527 int IOT_HTTP2_UploadFile_Request(void *http2_handle,
528                                  http2_upload_params_t *params,
529                                  http2_upload_result_cb_t *cb, void *user_data)
530 {
531     int ret;
532     http2_file_stream_t *file_node = NULL;
533 
534     if (http2_handle == NULL || params == NULL || cb == NULL) {
535         return NULL_VALUE_ERROR;
536     }
537 
538     if (params->file_path == NULL) {
539         return UPLOAD_FILE_PATH_IS_NULL;
540     }
541 
542     if ((params->opt_bit_map & UPLOAD_FILE_OPT_BIT_RESUME) &&
543         params->upload_id == NULL) {
544         return UPLOAD_ID_IS_NULL;
545     }
546 
547     if ((params->opt_bit_map & UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN) &&
548         params->upload_len == 0) {
549         return UPLOAD_LEN_IS_ZERO;
550     }
551 
552     file_node =
553         (http2_file_stream_t *)HTTP2_STREAM_MALLOC(sizeof(http2_file_stream_t));
554     if (file_node == NULL) {
555         return UPLOAD_MALLOC_FAILED;
556     }
557 
558     memset(file_node, 0, sizeof(http2_file_stream_t));
559     file_node->file_path = params->file_path;
560     file_node->part_len = params->part_len;
561     file_node->opened_cb = cb->upload_id_received_cb;
562     file_node->end_cb = cb->upload_completed_cb;
563     file_node->user_data = user_data;
564     file_node->type = FS_TYPE_NORMAL;
565     file_node->idx = g_http2_fs_ctx.upload_idx++;
566 
567     if (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN) {
568         file_node->spec_len = params->upload_len;
569     }
570     if (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_OVERWRITE) {
571         file_node->type = FS_TYPE_OVERRIDE;
572     } else if (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_RESUME) {
573         file_node->type = FS_TYPE_CONTINUE;
574         memcpy(file_node->upload_id, params->upload_id,
575                sizeof(file_node->upload_id));
576     }
577 
578     /* inset http2_fs node */
579     _http2_fs_list_insert(&g_http2_fs_ctx, file_node);
580 
581     if (g_http2_fs_ctx.file_thread == NULL) {
582         hal_os_thread_param_t thread_parms = { 0 };
583         thread_parms.stack_size = 6144;
584         thread_parms.name = "file_upload";
585         ret =
586             HAL_ThreadCreate(&g_http2_fs_ctx.file_thread, http_upload_file_func,
587                              (void *)&g_http2_fs_ctx, &thread_parms, NULL);
588         if (ret != 0) {
589             h2_err("file upload thread create error\n");
590             return -1;
591         }
592     }
593 
594     return SUCCESS_RETURN;
595 }
596 
IOT_HTTP2_UploadFile_Disconnect(void * handle)597 int IOT_HTTP2_UploadFile_Disconnect(void *handle)
598 {
599     int res = FAIL_RETURN;
600 
601     res = IOT_HTTP2_Disconnect(handle);
602 
603     if (g_http2_fs_ctx.list_mutex == NULL) {
604         memset(&g_http2_fs_ctx, 0, sizeof(g_http2_fs_ctx));
605     } else {
606         http2_file_stream_t *node, *next;
607         HAL_MutexLock(g_http2_fs_ctx.list_mutex);
608         list_for_each_entry_safe(node, next, &g_http2_fs_ctx.file_list, list,
609                                  http2_file_stream_t)
610         {
611             list_del((list_head_t *)&node->list);
612             HTTP2_STREAM_FREE(node);
613             break;
614         }
615         HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
616 
617         HAL_MutexDestroy(g_http2_fs_ctx.list_mutex);
618         memset(&g_http2_fs_ctx, 0, sizeof(g_http2_fs_ctx));
619     }
620 
621     return res;
622 }
623 
624 #endif /* #ifdef FS_ENABLED */
625