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