mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-18 00:45:55 +08:00
1.新增esp-idf设备端sdk
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#ifndef IOT_EXPORT_HTTP2_STREAM_H
|
||||
#define IOT_EXPORT_HTTP2_STREAM_H
|
||||
|
||||
#include "infra_types.h"
|
||||
#include "infra_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define IOT_HTTP2_RES_OVERTIME_MS (10000)
|
||||
#define IOT_HTTP2_KEEP_ALIVE_CNT (1)
|
||||
#define IOT_HTTP2_KEEP_ALIVE_TIME (30*1000) /* in seconds */
|
||||
|
||||
#define MAKE_HEADER(NAME, VALUE) \
|
||||
{ \
|
||||
(char *) NAME, (char *)VALUE, sizeof(NAME) - 1, sizeof(VALUE) - 1 \
|
||||
}
|
||||
|
||||
#define MAKE_HEADER_CS(NAME, VALUE) \
|
||||
{ \
|
||||
(char *) NAME, (char *)VALUE, strlen(NAME) , strlen(VALUE) \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *product_key;
|
||||
char *device_name;
|
||||
char *device_secret;
|
||||
char *url;
|
||||
int port;
|
||||
} device_conn_info_t;
|
||||
|
||||
typedef struct http2_header_struct {
|
||||
char *name; /* header name */
|
||||
char *value; /* the value of name */
|
||||
int namelen; /* the length of header name */
|
||||
int valuelen; /* the length of value */
|
||||
} http2_header;
|
||||
|
||||
typedef struct {
|
||||
http2_header *nva;
|
||||
int num;
|
||||
} header_ext_info_t;
|
||||
|
||||
typedef enum {
|
||||
STREAM_TYPE_DOWNLOAD,
|
||||
STREAM_TYPE_UPLOAD,
|
||||
STREAM_TYPE_AUXILIARY,
|
||||
STREAM_TYPE_NUM
|
||||
} stream_type_t;
|
||||
|
||||
typedef void (*on_stream_header_callback)(uint32_t stream_id, char *channel_id, int cat, const uint8_t *name,
|
||||
uint32_t namelen, const uint8_t *value, uint32_t valuelen, uint8_t flags, void *user_data);
|
||||
|
||||
typedef void (*on_stream_chunk_recv_callback)(uint32_t stream_id, char *channel_id,
|
||||
const uint8_t *data, uint32_t len, uint8_t flags, void *user_data);
|
||||
|
||||
typedef void (*on_stream_close_callback)(uint32_t stream_id, char *channel_id, uint32_t error_code, void *user_data);
|
||||
|
||||
typedef void (*on_stream_frame_send_callback)(uint32_t stream_id, char *channel_id, int type, uint8_t flags, void *user_data);
|
||||
|
||||
typedef void (*on_stream_frame_recv_callback)(uint32_t stream_id, char *channel_id, int type, uint8_t flags,void *user_data);
|
||||
|
||||
typedef void (*on_reconnect_callback)();
|
||||
typedef void (*on_disconnect_callback)();
|
||||
|
||||
typedef struct {
|
||||
on_stream_header_callback on_stream_header_cb;
|
||||
on_stream_chunk_recv_callback on_stream_chunk_recv_cb;
|
||||
on_stream_close_callback on_stream_close_cb;
|
||||
on_stream_frame_send_callback on_stream_frame_send_cb;
|
||||
on_stream_frame_recv_callback on_stream_frame_recv_cb;
|
||||
on_reconnect_callback on_reconnect_cb;
|
||||
on_disconnect_callback on_disconnect_cb;
|
||||
} http2_stream_cb_t;
|
||||
|
||||
typedef struct {
|
||||
char *stream; /* point to stream data buffer */
|
||||
uint32_t stream_len; /* file content length */
|
||||
uint32_t send_len; /* data had sent length */
|
||||
uint32_t packet_len; /* one packet length */
|
||||
const char *identify; /* path string to identify a stream service */
|
||||
int h2_stream_id; /* stream identifier which is a field in HTTP2 frame */
|
||||
char *channel_id; /* string return by server to identify a specific stream channel,
|
||||
different from stream identifier which is a field in HTTP2 frame */
|
||||
void *user_data; /* user data brought in at stream open */
|
||||
} stream_data_info_t;
|
||||
|
||||
DLL_IOT_API void *IOT_HTTP2_Connect(device_conn_info_t *conn_info, http2_stream_cb_t *user_cb);
|
||||
DLL_IOT_API int IOT_HTTP2_Stream_Open(void *handle, stream_data_info_t *info, header_ext_info_t *header);
|
||||
DLL_IOT_API int IOT_HTTP2_Stream_Send(void *handle, stream_data_info_t *info, header_ext_info_t *header);
|
||||
DLL_IOT_API int IOT_HTTP2_Stream_Query(void *handle, stream_data_info_t *info, header_ext_info_t *header);
|
||||
DLL_IOT_API int IOT_HTTP2_Stream_Close(void *handle, stream_data_info_t *info);
|
||||
DLL_IOT_API int IOT_HTTP2_Stream_Send_Message(void *handle, const char *identify,char *channel_id, char *data,
|
||||
uint32_t data_len, header_ext_info_t *header);
|
||||
DLL_IOT_API int IOT_HTTP2_Disconnect(void *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* IOT_EXPORT_FILE_UPLOADER_H */
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#ifndef _HTTP2_CONFIG_H
|
||||
#define _HTTP2_CONFIG_H
|
||||
|
||||
/* maximum packet len in one http2 frame */
|
||||
#ifndef FS_UPLOAD_PACKET_LEN
|
||||
#define FS_UPLOAD_PACKET_LEN 10240 /* must < 16384 */
|
||||
#endif
|
||||
|
||||
/* maximum content len of the http2 request */
|
||||
#ifndef FS_UPLOAD_PART_LEN
|
||||
#define FS_UPLOAD_PART_LEN (1024 * 1024 * 4) /* 100KB ~ 100MB */
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* #ifdef _HTTP2_CONFIG_H */
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#ifndef IOT_EXPORT_HTTP2_H
|
||||
#define IOT_EXPORT_HTTP2_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "http2_api.h"
|
||||
#include "http2_wrapper.h"
|
||||
|
||||
#ifdef INFRA_LOG
|
||||
#include "infra_log.h"
|
||||
#define h2_emerg(...) log_emerg("h2", __VA_ARGS__)
|
||||
#define h2_crit(...) log_crit("h2", __VA_ARGS__)
|
||||
#define h2_err(...) log_err("h2", __VA_ARGS__)
|
||||
#define h2_warning(...) log_warning("h2", __VA_ARGS__)
|
||||
#define h2_info(...) log_info("h2", __VA_ARGS__)
|
||||
#define h2_debug(...) log_debug("h2", __VA_ARGS__)
|
||||
#else
|
||||
#define h2_emerg(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
||||
#define h2_crit(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
||||
#define h2_err(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
||||
#define h2_warning(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
||||
#define h2_info(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
||||
#define h2_debug(...) do{HAL_Printf(__VA_ARGS__);HAL_Printf("\r\n");}while(0)
|
||||
#endif
|
||||
|
||||
#ifdef INFRA_MEM_STATS
|
||||
#include "infra_mem_stats.h"
|
||||
#define HTTP2_STREAM_MALLOC(size) LITE_malloc(size, MEM_MAGIC, "http2.stream")
|
||||
#define HTTP2_STREAM_FREE(ptr) LITE_free(ptr)
|
||||
#else
|
||||
#define HTTP2_STREAM_MALLOC(size) HAL_Malloc(size)
|
||||
#define HTTP2_STREAM_FREE(ptr) do{if(ptr != NULL) {HAL_Free(ptr);ptr = NULL;}}while(0)
|
||||
#endif /* #ifdef INFRA_MEM_STATS */
|
||||
|
||||
#define POINTER_SANITY_CHECK(ptr, err) \
|
||||
do { \
|
||||
if (NULL == (ptr)) { \
|
||||
return (err); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ARGUMENT_SANITY_CHECK(expr, err) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
return (err); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
typedef enum {
|
||||
|
||||
HTTP2_FLAG_NONE = 0,
|
||||
|
||||
HTTP2_FLAG_END_STREAM = 0x01,
|
||||
|
||||
} http2_flag;
|
||||
|
||||
typedef struct http2_list_s {
|
||||
struct http2_list_s *prev;
|
||||
struct http2_list_s *next;
|
||||
} http2_list_t;
|
||||
|
||||
typedef void (*on_user_header_callback)(int32_t stream_id, int cat, const uint8_t *name, uint32_t namelen,
|
||||
const uint8_t *value, uint32_t valuelen, uint8_t flags);
|
||||
|
||||
typedef void (*on_user_chunk_recv_callback)(int32_t stream_id,
|
||||
const uint8_t *data, uint32_t len, uint8_t flags);
|
||||
|
||||
typedef void (*on_user_stream_close_callback)(int32_t stream_id, uint32_t error_code);
|
||||
|
||||
typedef void (*on_user_frame_send_callback)(int32_t stream_id, int type, uint8_t flags);
|
||||
|
||||
typedef void (*on_user_frame_recv_callback)(int32_t stream_id, int type, uint8_t flags);
|
||||
|
||||
typedef struct {
|
||||
on_user_header_callback on_user_header_cb;
|
||||
on_user_chunk_recv_callback on_user_chunk_recv_cb;
|
||||
on_user_stream_close_callback on_user_stream_close_cb;
|
||||
on_user_frame_send_callback on_user_frame_send_cb;
|
||||
on_user_frame_recv_callback on_user_frame_recv_cb;
|
||||
} http2_user_cb_t;
|
||||
|
||||
typedef struct http2_connection {
|
||||
void *network; /* iot network ptr */
|
||||
void *session; /* http2 session */
|
||||
int flag; /* check the stream is end or not */
|
||||
int status;
|
||||
http2_user_cb_t *cbs;
|
||||
} http2_connection_t;
|
||||
|
||||
typedef struct http2_data_struct {
|
||||
http2_header *header; /* header data. */
|
||||
int header_count; /* the count of header data. */
|
||||
char *data; /* send data. */
|
||||
int len; /* send data length. */
|
||||
int stream_id; /* send data over specify stream */
|
||||
int flag; /* send data flag. */
|
||||
} http2_data;
|
||||
|
||||
typedef struct {
|
||||
http2_connection_t *http2_connect;
|
||||
void *mutex;
|
||||
void *semaphore;
|
||||
void *rw_thread;
|
||||
http2_list_t stream_list;
|
||||
int init_state;
|
||||
http2_stream_cb_t *cbs;
|
||||
uint8_t connect_state;
|
||||
uint8_t retry_cnt;
|
||||
} stream_handle_t;
|
||||
|
||||
#ifdef FS_ENABLED
|
||||
|
||||
typedef struct {
|
||||
char fs_upload_id[50];
|
||||
int fs_offset;
|
||||
} fs_rsp_header_val_t;
|
||||
|
||||
int IOT_HTTP2_FS_Close(void *hd, stream_data_info_t *info, header_ext_info_t *header);
|
||||
|
||||
#endif /* #ifdef FS_ENABLED */
|
||||
|
||||
/**
|
||||
* @brief the http2 client connect.
|
||||
* @param[in] pclient: http client. <struct httpclient_t>
|
||||
* @return http2 client connection handler.
|
||||
*/
|
||||
extern http2_connection_t *iotx_http2_client_connect(void *pclient, char *url, int port);
|
||||
|
||||
http2_connection_t *iotx_http2_client_connect_with_cb(void *pclient, char *url, int port, http2_user_cb_t *cb);
|
||||
/**
|
||||
* @brief the http2 client send data.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @param[in] data: send data.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
extern int iotx_http2_client_send(http2_connection_t *conn, http2_data *h2_data);
|
||||
/**
|
||||
* @brief the http2 client receive data.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @param[in] data: receive data buffer.
|
||||
* @param[in] data_len: buffer length.
|
||||
* @param[in] len: receive data length.
|
||||
* @param[in] timeout: receive data timeout.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
extern int iotx_http2_client_recv(http2_connection_t *conn, char *data, int data_len, int *len, int timeout);
|
||||
/**
|
||||
* @brief the http2 client connect.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
extern int iotx_http2_client_disconnect(http2_connection_t *conn);
|
||||
/**
|
||||
* @brief the http2 client send ping to keep alive.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
extern int iotx_http2_client_send_ping(http2_connection_t *conn);
|
||||
/**
|
||||
* @brief the http2 client get available windows size.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @return The window size.
|
||||
*/
|
||||
extern int iotx_http2_get_available_window_size(http2_connection_t *conn);
|
||||
/**
|
||||
* @brief the http2 client receive windows size packet to update window.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
extern int iotx_http2_update_window_size(http2_connection_t *conn);
|
||||
/**
|
||||
* @brief the http2 client performs the network I/O.
|
||||
* @param[in] handler: http2 client connection handler.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
extern int iotx_http2_exec_io(http2_connection_t *connection);
|
||||
|
||||
extern int iotx_http2_client_recv_ping(void);
|
||||
|
||||
int iotx_http2_reset_stream(http2_connection_t *connection, int32_t stream_id);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* IOT_EXPORT_HTTP2_H */
|
||||
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#include "infra_config.h"
|
||||
#ifdef FS_ENABLED
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "infra_defs.h"
|
||||
#include "infra_types.h"
|
||||
#include "infra_httpc.h"
|
||||
#include "infra_sha1.h"
|
||||
#include "infra_timer.h"
|
||||
#include "infra_list.h"
|
||||
#include "infra_log.h"
|
||||
|
||||
#include "http2_internal.h"
|
||||
#include "http2_api.h"
|
||||
#include "http2_upload_api.h"
|
||||
#include "http2_config.h"
|
||||
#include "http2_wrapper.h"
|
||||
|
||||
#include "wrappers_defs.h"
|
||||
|
||||
#define PACKET_LEN 16384
|
||||
#define HTTP2_FS_SERVICE_ID "c/iot/sys/thing/file/upload"
|
||||
|
||||
|
||||
typedef enum {
|
||||
FS_STATUS_WAITING,
|
||||
FS_STATUS_UPLOADING,
|
||||
FS_STATUS_BREAK,
|
||||
FS_STATUS_END,
|
||||
FS_STATUS_NUM
|
||||
} file_stream_status_t;
|
||||
|
||||
typedef enum {
|
||||
FS_TYPE_NORMAL,
|
||||
FS_TYPE_CONTINUE,
|
||||
FS_TYPE_OVERRIDE,
|
||||
} file_stream_type_t;
|
||||
|
||||
typedef struct {
|
||||
int idx;
|
||||
const char *file_path;
|
||||
char upload_id[40];
|
||||
uint32_t spec_len;
|
||||
uint32_t file_offset;
|
||||
uint32_t part_len;
|
||||
file_stream_type_t type;
|
||||
http2_upload_id_received_cb_t opened_cb;
|
||||
http2_upload_completed_cb_t end_cb;
|
||||
void *user_data;
|
||||
|
||||
uint8_t if_stop;
|
||||
|
||||
http2_list_t list;
|
||||
} http2_file_stream_t;
|
||||
|
||||
typedef struct {
|
||||
stream_handle_t *http2_handle;
|
||||
const char *service_id;
|
||||
http2_list_t file_list;
|
||||
void *list_mutex;
|
||||
void *file_thread;
|
||||
int upload_idx;
|
||||
} http2_file_stream_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *send_buffer;
|
||||
const char *upload_id;
|
||||
uint32_t file_offset;
|
||||
uint32_t part_len;
|
||||
} fs_send_ext_info_t;
|
||||
|
||||
|
||||
static http2_file_stream_ctx_t g_http2_fs_ctx = { 0 };
|
||||
static http2_stream_cb_t callback_func = { 0 };
|
||||
|
||||
|
||||
/* utils, get file sizef */
|
||||
static int http2_stream_get_file_size(const char *file_name)
|
||||
{
|
||||
void *fp = NULL;
|
||||
int size = 0;
|
||||
if ((fp = HAL_Fopen(file_name, "r")) == NULL) {
|
||||
h2_err("The file %s can not be opened.\n", file_name);
|
||||
return -1;
|
||||
}
|
||||
HAL_Fseek(fp, 0L, HAL_SEEK_END);
|
||||
size = HAL_Ftell(fp);
|
||||
HAL_Fclose(fp);
|
||||
return size;
|
||||
}
|
||||
|
||||
/* utils, get file name */
|
||||
static const char *_http2_fs_get_filename(const char *file_path)
|
||||
{
|
||||
const char *p_name = NULL;
|
||||
|
||||
if (file_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p_name = file_path + strlen(file_path);
|
||||
|
||||
while (--p_name != file_path) {
|
||||
if (*p_name == '/') {
|
||||
p_name++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p_name;
|
||||
}
|
||||
|
||||
/* utils, read file data */
|
||||
static int http2_stream_get_file_data(const char *file_name, char *data, int len, int offset)
|
||||
{
|
||||
void *fp = NULL;
|
||||
int ret = 0;
|
||||
if ((fp = HAL_Fopen(file_name, "r")) == NULL) {
|
||||
h2_err("The file %s can not be opened.\n", file_name);
|
||||
return -1;
|
||||
}
|
||||
ret = HAL_Fseek(fp, offset, HAL_SEEK_SET);
|
||||
if (ret != 0) {
|
||||
HAL_Fclose(fp);
|
||||
h2_err("The file %s can not move offset.\n", file_name);
|
||||
return -1;
|
||||
}
|
||||
ret = HAL_Fread(data, len, 1, fp);
|
||||
HAL_Fclose(fp);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* open http2 file upload channel */
|
||||
static int _http2_fs_open_channel(http2_file_stream_t *fs_node, stream_data_info_t *info)
|
||||
{
|
||||
stream_handle_t *h2_handle = g_http2_fs_ctx.http2_handle;
|
||||
header_ext_info_t ext_header;
|
||||
fs_rsp_header_val_t *open_rsp = (fs_rsp_header_val_t *)info->user_data;
|
||||
const char *filename = NULL;
|
||||
int ret = UPLOAD_ERROR_COMMON;
|
||||
|
||||
/* header for normal upload */
|
||||
http2_header header_filename[] = {
|
||||
MAKE_HEADER_CS("x-file-name", ""),
|
||||
};
|
||||
|
||||
/* header for override operation */
|
||||
http2_header header_overwrite[] = {
|
||||
MAKE_HEADER_CS("x-file-name", ""),
|
||||
MAKE_HEADER_CS("x-file-overwrite", "1"),
|
||||
};
|
||||
|
||||
/* header for continue operation */
|
||||
http2_header header_uploadid[] = {
|
||||
MAKE_HEADER("x-file-upload-id", ""),
|
||||
};
|
||||
|
||||
filename = _http2_fs_get_filename(fs_node->file_path);
|
||||
h2_info("filename = %s", filename);
|
||||
|
||||
header_filename[0].value = (char *)filename;
|
||||
header_filename[0].valuelen = strlen(filename);
|
||||
header_overwrite[0].value = (char *)filename;
|
||||
header_overwrite[0].valuelen = strlen(filename);
|
||||
|
||||
/* setup http2 ext_header */
|
||||
switch (fs_node->type) {
|
||||
case FS_TYPE_NORMAL: {
|
||||
ext_header.num = 1;
|
||||
ext_header.nva = header_filename;
|
||||
|
||||
} break;
|
||||
case FS_TYPE_OVERRIDE: {
|
||||
ext_header.num = 2;
|
||||
ext_header.nva = header_overwrite;
|
||||
|
||||
} break;
|
||||
case FS_TYPE_CONTINUE: {
|
||||
/* upload id must be exist */
|
||||
header_uploadid[0].value = fs_node->upload_id;
|
||||
header_uploadid[0].valuelen = strlen(fs_node->upload_id);
|
||||
ext_header.num = 1;
|
||||
ext_header.nva = header_uploadid;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
ret = IOT_HTTP2_Stream_Open(h2_handle, info, &ext_header);
|
||||
if (ret < 0 || open_rsp->fs_offset == -1 || open_rsp->fs_upload_id[0] == '\0') {
|
||||
h2_err("IOT_HTTP2_Stream_Open failed %d\n", ret);
|
||||
return UPLOAD_STREAM_OPEN_FAILED;
|
||||
}
|
||||
h2_info("fs channel open succeed");
|
||||
return SUCCESS_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/* file part data send sync api */
|
||||
static int _http2_fs_part_send_sync(http2_file_stream_t *fs_node, stream_data_info_t *info, fs_send_ext_info_t *ext_info)
|
||||
{
|
||||
stream_handle_t *h2_handle = g_http2_fs_ctx.http2_handle;
|
||||
header_ext_info_t ext_header;
|
||||
int res;
|
||||
http2_header header_uploadid[] = {
|
||||
MAKE_HEADER_CS("x-file-upload-id", ext_info->upload_id),
|
||||
};
|
||||
|
||||
/* setup ext header */
|
||||
ext_header.num = 1;
|
||||
ext_header.nva = header_uploadid;
|
||||
|
||||
/* setup Stream_Send params */
|
||||
info->stream = ext_info->send_buffer;
|
||||
info->stream_len = ext_info->part_len;
|
||||
info->send_len = 0;
|
||||
info->packet_len = FS_UPLOAD_PACKET_LEN;
|
||||
|
||||
while (info->send_len < info->stream_len) {
|
||||
if (!h2_handle->init_state) {
|
||||
res = UPLOAD_ERROR_COMMON;
|
||||
break;
|
||||
}
|
||||
|
||||
res = http2_stream_get_file_data(fs_node->file_path, ext_info->send_buffer, FS_UPLOAD_PACKET_LEN, (info->send_len + ext_info->file_offset)); /* offset used */
|
||||
if (res <= 0) {
|
||||
res = UPLOAD_FILE_READ_FAILED;
|
||||
break;
|
||||
}
|
||||
info->packet_len = res;
|
||||
|
||||
/* adjust the packet len */
|
||||
if (info->stream_len - info->send_len < info->packet_len) {
|
||||
info->packet_len = info->stream_len - info->send_len;
|
||||
}
|
||||
|
||||
res = IOT_HTTP2_Stream_Send(h2_handle, info, &ext_header);
|
||||
if (res < 0) {
|
||||
res = UPLOAD_STREAM_SEND_FAILED;
|
||||
break;
|
||||
}
|
||||
h2_debug("send len = %d\n", info->send_len);
|
||||
|
||||
if (fs_node->if_stop) {
|
||||
res = UPLOAD_STOP_BY_IOCTL;
|
||||
break;
|
||||
}
|
||||
|
||||
res = SUCCESS_RETURN;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void *_http2_fs_node_handle(http2_file_stream_t *fs_node)
|
||||
{
|
||||
stream_handle_t *h2_handle = g_http2_fs_ctx.http2_handle;
|
||||
int filesize = 0;
|
||||
int upload_len = 0;
|
||||
fs_rsp_header_val_t rsp_data;
|
||||
fs_send_ext_info_t send_ext_info;
|
||||
stream_data_info_t channel_info;
|
||||
uint32_t part_len = 0;
|
||||
int res = FAIL_RETURN;
|
||||
|
||||
/* params check */
|
||||
if (h2_handle == NULL) {
|
||||
/* TODO: handle */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get fileszie */
|
||||
filesize = http2_stream_get_file_size(fs_node->file_path);
|
||||
if (filesize <= 0) {
|
||||
if (fs_node->end_cb) {
|
||||
fs_node->end_cb(fs_node->file_path, UPLOAD_FILE_NOT_EXIST, fs_node->user_data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
h2_info("filesize = %d", filesize);
|
||||
|
||||
/* open http2 file upload channel */
|
||||
memset(&rsp_data, 0, sizeof(fs_rsp_header_val_t));
|
||||
memset(&channel_info, 0, sizeof(stream_data_info_t));
|
||||
channel_info.identify = g_http2_fs_ctx.service_id;
|
||||
channel_info.user_data = (void *)&rsp_data;
|
||||
|
||||
res = _http2_fs_open_channel(fs_node, &channel_info);
|
||||
if (res < SUCCESS_RETURN) {
|
||||
if (fs_node->end_cb) {
|
||||
fs_node->end_cb(fs_node->file_path, res, fs_node->user_data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h2_info("upload_id = %s", rsp_data.fs_upload_id);
|
||||
h2_info("upload_offset = %d", rsp_data.fs_offset);
|
||||
if (fs_node->opened_cb) {
|
||||
fs_node->opened_cb(fs_node->file_path, rsp_data.fs_upload_id, fs_node->user_data);
|
||||
}
|
||||
|
||||
if (fs_node->spec_len && (fs_node->spec_len + rsp_data.fs_offset < filesize)) {
|
||||
upload_len = fs_node->spec_len + rsp_data.fs_offset;
|
||||
}
|
||||
else {
|
||||
upload_len = filesize;
|
||||
}
|
||||
|
||||
/* setup send part len */
|
||||
if ((fs_node->part_len < (1024 * 100)) || (fs_node->part_len > (1024 * 1024 * 100))) {
|
||||
part_len = FS_UPLOAD_PART_LEN;
|
||||
}
|
||||
else {
|
||||
part_len = fs_node->part_len;
|
||||
}
|
||||
|
||||
/* send http2 file upload data */
|
||||
send_ext_info.upload_id = rsp_data.fs_upload_id;
|
||||
send_ext_info.file_offset = rsp_data.fs_offset;
|
||||
send_ext_info.send_buffer = HTTP2_STREAM_MALLOC(FS_UPLOAD_PACKET_LEN);
|
||||
if (send_ext_info.send_buffer == NULL) {
|
||||
if (fs_node->end_cb) {
|
||||
fs_node->end_cb(fs_node->file_path, UPLOAD_MALLOC_FAILED, fs_node->user_data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
/* setup the part len */
|
||||
send_ext_info.part_len = ((upload_len - send_ext_info.file_offset) < part_len)?
|
||||
(upload_len - send_ext_info.file_offset): part_len;
|
||||
|
||||
res = _http2_fs_part_send_sync(fs_node, &channel_info, &send_ext_info);
|
||||
if (res < SUCCESS_RETURN) {
|
||||
h2_err("fs send return %d", res);
|
||||
break;
|
||||
}
|
||||
|
||||
send_ext_info.file_offset += send_ext_info.part_len;
|
||||
h2_info("file offset = %d now", send_ext_info.file_offset);
|
||||
} while (send_ext_info.file_offset < upload_len);
|
||||
|
||||
if (res < 0) {
|
||||
if (fs_node->end_cb) {
|
||||
fs_node->end_cb(fs_node->file_path, res, fs_node->user_data);
|
||||
}
|
||||
|
||||
HTTP2_STREAM_FREE(channel_info.channel_id);
|
||||
HTTP2_STREAM_FREE(send_ext_info.send_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* close http2 file upload channel */
|
||||
IOT_HTTP2_FS_Close(h2_handle, &channel_info, NULL);
|
||||
|
||||
if (fs_node->end_cb) {
|
||||
fs_node->end_cb(fs_node->file_path, UPLOAD_SUCCESS, fs_node->user_data);
|
||||
}
|
||||
|
||||
HTTP2_STREAM_FREE(send_ext_info.send_buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *http_upload_file_func(void *fs_data)
|
||||
{
|
||||
http2_file_stream_ctx_t *fs_ctx = (http2_file_stream_ctx_t *)fs_data;
|
||||
http2_file_stream_t *node = NULL;
|
||||
if (fs_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (fs_ctx->http2_handle->init_state) {
|
||||
HAL_MutexLock(fs_ctx->list_mutex);
|
||||
if (!list_empty((list_head_t *)&g_http2_fs_ctx.file_list)) {
|
||||
node = list_first_entry(&fs_ctx->file_list, http2_file_stream_t, list);
|
||||
HAL_MutexUnlock(fs_ctx->list_mutex);
|
||||
|
||||
/* execute upload routine */
|
||||
_http2_fs_node_handle((void *)node);
|
||||
|
||||
/* delete the completed node */
|
||||
HAL_MutexLock(fs_ctx->list_mutex);
|
||||
list_del((list_head_t *)&node->list);
|
||||
HTTP2_STREAM_FREE(node);
|
||||
HAL_MutexUnlock(fs_ctx->list_mutex);
|
||||
}
|
||||
else {
|
||||
HAL_MutexUnlock(fs_ctx->list_mutex);
|
||||
h2_debug("file list is empty, file upload thread exit\n");
|
||||
g_http2_fs_ctx.file_thread = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _http2_fs_list_insert(http2_file_stream_ctx_t *fs_ctx, http2_file_stream_t *node)
|
||||
{
|
||||
INIT_LIST_HEAD((list_head_t *)&node->list);
|
||||
HAL_MutexLock(fs_ctx->list_mutex);
|
||||
list_add_tail((list_head_t *)&node->list, (list_head_t *)&fs_ctx->file_list);
|
||||
HAL_MutexUnlock(fs_ctx->list_mutex);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
HTTP2_IOCTL_STOP_UPLOAD,
|
||||
HTTP2_IOCTL_COMMAND_NUM,
|
||||
} http2_file_upload_ioctl_command_t;
|
||||
|
||||
static int _http2_fs_list_search_by_idx(int idx, http2_file_stream_t **search_node)
|
||||
{
|
||||
http2_file_stream_t *node = NULL;
|
||||
|
||||
HAL_MutexLock(g_http2_fs_ctx.list_mutex);
|
||||
|
||||
list_for_each_entry(node, &g_http2_fs_ctx.file_list, list, http2_file_stream_t) {
|
||||
if (idx == node->idx) {
|
||||
*search_node = node;
|
||||
HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
|
||||
return SUCCESS_RETURN;
|
||||
}
|
||||
}
|
||||
|
||||
HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
|
||||
*search_node = NULL;
|
||||
return FAIL_RETURN;
|
||||
}
|
||||
|
||||
int IOT_HTTP2_Ioctl(int upload_idx, int command, void *data)
|
||||
{
|
||||
http2_file_stream_t *node = NULL;
|
||||
|
||||
if (g_http2_fs_ctx.http2_handle == NULL) {
|
||||
return UPLOAD_ERROR_COMMON;
|
||||
}
|
||||
|
||||
_http2_fs_list_search_by_idx(upload_idx, &node);
|
||||
if (node == NULL) {
|
||||
return UPLOAD_ERROR_COMMON;
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case HTTP2_IOCTL_STOP_UPLOAD: {
|
||||
if (g_http2_fs_ctx.http2_handle) {
|
||||
HAL_MutexLock(g_http2_fs_ctx.list_mutex);
|
||||
node->if_stop = 1;
|
||||
HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
|
||||
return SUCCESS_RETURN;
|
||||
}
|
||||
else {
|
||||
return UPLOAD_ERROR_COMMON;
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
return UPLOAD_ERROR_COMMON;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS_RETURN;
|
||||
}
|
||||
|
||||
void *IOT_HTTP2_UploadFile_Connect(http2_upload_conn_info_t *conn_info, http2_status_cb_t *cb)
|
||||
{
|
||||
void *handle;
|
||||
|
||||
memset(&callback_func, 0, sizeof(http2_stream_cb_t));
|
||||
|
||||
if (cb != NULL) {
|
||||
callback_func.on_reconnect_cb = cb->on_reconnect_cb;
|
||||
callback_func.on_disconnect_cb = cb->on_disconnect_cb;
|
||||
}
|
||||
|
||||
handle = IOT_HTTP2_Connect((device_conn_info_t *)conn_info, &callback_func);
|
||||
if (handle == NULL) {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
g_http2_fs_ctx.list_mutex = HAL_MutexCreate();
|
||||
if (g_http2_fs_ctx.list_mutex == NULL) {
|
||||
h2_err("fs mutex create error\n");
|
||||
IOT_HTTP2_UploadFile_Disconnect(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD((list_head_t *)&(g_http2_fs_ctx.file_list));
|
||||
g_http2_fs_ctx.http2_handle = handle;
|
||||
g_http2_fs_ctx.service_id = HTTP2_FS_SERVICE_ID;
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int IOT_HTTP2_UploadFile_Request(void *http2_handle, http2_upload_params_t *params, http2_upload_result_cb_t *cb, void *user_data)
|
||||
{
|
||||
int ret;
|
||||
http2_file_stream_t *file_node = NULL;
|
||||
|
||||
if (http2_handle == NULL || params == NULL || cb == NULL) {
|
||||
return NULL_VALUE_ERROR;
|
||||
}
|
||||
|
||||
if (params->file_path == NULL) {
|
||||
return UPLOAD_FILE_PATH_IS_NULL;
|
||||
}
|
||||
|
||||
if ( (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_RESUME) && params->upload_id == NULL) {
|
||||
return UPLOAD_ID_IS_NULL;
|
||||
}
|
||||
|
||||
if ( (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN) && params->upload_len == 0) {
|
||||
return UPLOAD_LEN_IS_ZERO;
|
||||
}
|
||||
|
||||
file_node = (http2_file_stream_t *)HTTP2_STREAM_MALLOC(sizeof(http2_file_stream_t));
|
||||
if (file_node == NULL) {
|
||||
return UPLOAD_MALLOC_FAILED;
|
||||
}
|
||||
|
||||
memset(file_node, 0, sizeof(http2_file_stream_t));
|
||||
file_node->file_path = params->file_path;
|
||||
file_node->part_len = params->part_len;
|
||||
file_node->opened_cb = cb->upload_id_received_cb;
|
||||
file_node->end_cb = cb->upload_completed_cb;
|
||||
file_node->user_data = user_data;
|
||||
file_node->type = FS_TYPE_NORMAL;
|
||||
file_node->idx = g_http2_fs_ctx.upload_idx++;
|
||||
|
||||
if (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN) {
|
||||
file_node->spec_len = params->upload_len;
|
||||
}
|
||||
if (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_OVERWRITE) {
|
||||
file_node->type = FS_TYPE_OVERRIDE;
|
||||
}
|
||||
else if (params->opt_bit_map & UPLOAD_FILE_OPT_BIT_RESUME) {
|
||||
file_node->type = FS_TYPE_CONTINUE;
|
||||
memcpy(file_node->upload_id, params->upload_id, sizeof(file_node->upload_id));
|
||||
}
|
||||
|
||||
/* inset http2_fs node */
|
||||
_http2_fs_list_insert(&g_http2_fs_ctx, file_node);
|
||||
|
||||
if (g_http2_fs_ctx.file_thread == NULL) {
|
||||
hal_os_thread_param_t thread_parms = {0};
|
||||
thread_parms.stack_size = 6144;
|
||||
thread_parms.name = "file_upload";
|
||||
ret = HAL_ThreadCreate(&g_http2_fs_ctx.file_thread, http_upload_file_func, (void *)&g_http2_fs_ctx, &thread_parms, NULL);
|
||||
if (ret != 0) {
|
||||
h2_err("file upload thread create error\n");
|
||||
return -1;
|
||||
}
|
||||
HAL_ThreadDetach(g_http2_fs_ctx.file_thread);
|
||||
}
|
||||
|
||||
return SUCCESS_RETURN;
|
||||
}
|
||||
|
||||
int IOT_HTTP2_UploadFile_Disconnect(void *handle)
|
||||
{
|
||||
int res = FAIL_RETURN;
|
||||
|
||||
res = IOT_HTTP2_Disconnect(handle);
|
||||
|
||||
if (g_http2_fs_ctx.list_mutex == NULL) {
|
||||
memset(&g_http2_fs_ctx, 0, sizeof(g_http2_fs_ctx));
|
||||
}
|
||||
else {
|
||||
http2_file_stream_t *node, *next;
|
||||
HAL_MutexLock(g_http2_fs_ctx.list_mutex);
|
||||
list_for_each_entry_safe(node, next, &g_http2_fs_ctx.file_list, list, http2_file_stream_t) {
|
||||
list_del((list_head_t *)&node->list);
|
||||
HTTP2_STREAM_FREE(node);
|
||||
break;
|
||||
}
|
||||
HAL_MutexUnlock(g_http2_fs_ctx.list_mutex);
|
||||
|
||||
HAL_MutexDestroy(g_http2_fs_ctx.list_mutex);
|
||||
memset(&g_http2_fs_ctx, 0, sizeof(g_http2_fs_ctx));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* #ifdef FS_ENABLED */
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#ifndef _HTTP2_UPLOAD_API_H_
|
||||
#define _HTTP2_UPLOAD_API_H_
|
||||
|
||||
#include "infra_types.h"
|
||||
#include "infra_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* bit define of file override option */
|
||||
#define UPLOAD_FILE_OPT_BIT_OVERWRITE (0x00000001)
|
||||
#define UPLOAD_FILE_OPT_BIT_RESUME (0x00000002)
|
||||
#define UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN (0x00000004)
|
||||
|
||||
/* http2 connect information */
|
||||
typedef struct {
|
||||
char *product_key;
|
||||
char *device_name;
|
||||
char *device_secret;
|
||||
char *url;
|
||||
int port;
|
||||
} http2_upload_conn_info_t;
|
||||
|
||||
/* file upload option define */
|
||||
typedef struct {
|
||||
const char *file_path; /* file path, filename must be ASCII string and strlen < 2014 */
|
||||
uint32_t part_len; /* maximum content len of one http2 request, must be in range of 100KB ~ 100MB */
|
||||
const char *upload_id; /* a specific id used to indicate one upload session, only required when UPLOAD_FILE_OPT_BIT_RESUME option set */
|
||||
uint32_t upload_len; /* used to indicate the upload length, only required when UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN option set */
|
||||
uint32_t opt_bit_map; /* option bit map, support UPLOAD_FILE_OPT_BIT_OVERWRITE, UPLOAD_FILE_OPT_BIT_RESUME and UPLOAD_FILE_OPT_BIT_SPECIFIC_LEN */
|
||||
} http2_upload_params_t;
|
||||
|
||||
/* error code for file upload */
|
||||
typedef enum {
|
||||
UPLOAD_STOP_BY_IOCTL = -14,
|
||||
UPLOAD_HTTP2_HANDLE_NULL = -13,
|
||||
UPLOAD_LEN_IS_ZERO = -12,
|
||||
UPLOAD_FILE_PATH_IS_NULL = -11,
|
||||
UPLOAD_ID_IS_NULL = -10,
|
||||
UPLOAD_FILE_NOT_EXIST = -9,
|
||||
UPLOAD_FILE_READ_FAILED = -8,
|
||||
UPLOAD_STREAM_OPEN_FAILED = -7,
|
||||
UPLOAD_STREAM_SEND_FAILED = -6,
|
||||
UPLOAD_MALLOC_FAILED = -5,
|
||||
UPLOAD_NULL_POINT = -2,
|
||||
UPLOAD_ERROR_COMMON = -1,
|
||||
UPLOAD_SUCCESS = 0,
|
||||
} http2_file_upload_result_t;
|
||||
|
||||
/* gerneral callback function, this callback will be invoke when http2 disconnected */
|
||||
typedef void (*http2_disconnect_cb_t)(void);
|
||||
|
||||
/* gerneral callback function, this callback will be invoke when http2 reconnected */
|
||||
typedef void (*http2_reconnect_cb_t)(void);
|
||||
|
||||
/* callback function type define, this callback will be invoke when upload completed */
|
||||
typedef void (*http2_upload_completed_cb_t)(const char *file_path, int result, void *user_data);
|
||||
|
||||
/* callback funciton type define, this callback will be invoke when upload_id received */
|
||||
typedef void (*http2_upload_id_received_cb_t)(const char *file_path, const char *upload_id, void *user_data);
|
||||
|
||||
/* http2 connect status callback define */
|
||||
typedef struct {
|
||||
http2_disconnect_cb_t on_disconnect_cb;
|
||||
http2_reconnect_cb_t on_reconnect_cb;
|
||||
} http2_status_cb_t;
|
||||
|
||||
/* http2 upload result callback define */
|
||||
typedef struct {
|
||||
http2_upload_id_received_cb_t upload_id_received_cb;
|
||||
http2_upload_completed_cb_t upload_completed_cb;
|
||||
} http2_upload_result_cb_t;
|
||||
|
||||
/* http2 uploadfile connect api, http2 handle returned */
|
||||
DLL_IOT_API void *IOT_HTTP2_UploadFile_Connect(http2_upload_conn_info_t *conn_info, http2_status_cb_t *cb);
|
||||
|
||||
/* http2 uploadfile start api */
|
||||
DLL_IOT_API int IOT_HTTP2_UploadFile_Request(void *http2_handle, http2_upload_params_t *params, http2_upload_result_cb_t *cb, void *user_data);
|
||||
|
||||
/* http2 uploadfile disconnect api */
|
||||
DLL_IOT_API int IOT_HTTP2_UploadFile_Disconnect(void *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* #ifndef _HTTP2_UPLOAD_API_H_ */
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#ifndef __HTTP2_WRAPPER_H__
|
||||
#define __HTTP2_WRAPPER_H__
|
||||
|
||||
#include "infra_types.h"
|
||||
#include "infra_defs.h"
|
||||
#include "wrappers_defs.h"
|
||||
|
||||
extern void HAL_Printf(const char *fmt, ...);
|
||||
extern void HAL_SleepMs(uint32_t ms);
|
||||
|
||||
extern void *HAL_Malloc(uint32_t size);
|
||||
extern void *HAL_Realloc(void *ptr, uint32_t size);
|
||||
extern void HAL_Free(void *ptr);
|
||||
|
||||
extern void *HAL_MutexCreate(void);
|
||||
extern void HAL_MutexDestroy(void *mutex);
|
||||
extern void HAL_MutexLock(void *mutex);
|
||||
extern void HAL_MutexUnlock(void *mutex);
|
||||
|
||||
extern void *HAL_SemaphoreCreate(void);
|
||||
extern void HAL_SemaphoreDestroy(void *sem);
|
||||
extern void HAL_SemaphorePost(void *sem);
|
||||
extern int HAL_SemaphoreWait(void *sem, uint32_t timeout_ms);
|
||||
|
||||
extern int HAL_ThreadCreate(
|
||||
void **thread_handle,
|
||||
void *(*work_routine)(void *),
|
||||
void *arg,
|
||||
hal_os_thread_param_t *hal_os_thread_param,
|
||||
int *stack_used);
|
||||
extern void HAL_ThreadDetach(void *thread_handle);
|
||||
extern void HAL_ThreadDelete(void *thread_handle);
|
||||
|
||||
#ifdef FS_ENABLED
|
||||
typedef enum {
|
||||
HAL_SEEK_SET,
|
||||
HAL_SEEK_CUR,
|
||||
HAL_SEEK_END
|
||||
} hal_fs_seek_type_t;
|
||||
|
||||
extern void *HAL_Fopen(const char *path, const char *mode);
|
||||
extern uint32_t HAL_Fread(void *buff, uint32_t size, uint32_t count, void *stream);
|
||||
extern uint32_t HAL_Fwrite(const void *ptr, uint32_t size, uint32_t count, void *stream);
|
||||
extern int HAL_Fseek(void *stream, long offset, int framewhere);
|
||||
extern int HAL_Fclose(void *stream);
|
||||
extern long HAL_Ftell(void *stream);
|
||||
#endif /* #ifdef FS_ENABLED */
|
||||
|
||||
#endif /* #ifndef __HTTP2_WRAPPER_H__ */
|
||||
|
||||
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nghttp2.h"
|
||||
#include "nghttp2_session.h"
|
||||
#include "infra_httpc.h"
|
||||
#include "http2_internal.h"
|
||||
#include "http2_wrapper.h"
|
||||
|
||||
|
||||
#define MAX_HTTP2_HOST_LEN (128)
|
||||
#define NGHTTP2_DBG h2_info
|
||||
|
||||
typedef enum {
|
||||
PING_IDLE,
|
||||
PING_SENDING,
|
||||
PING_SENT,
|
||||
PING_RECVED,
|
||||
} http2_ping_state_t;
|
||||
|
||||
|
||||
enum { IO_NONE, WANT_READ, WANT_WRITE };
|
||||
|
||||
typedef struct _http2_request_struct_ {
|
||||
/* Stream ID for this request. */
|
||||
int32_t stream_id;
|
||||
} http2_request;
|
||||
|
||||
|
||||
extern const char *iotx_ca_get(void);
|
||||
extern int httpclient_connect(httpclient_t *client);
|
||||
static int http2_nv_copy_nghttp2_nv(nghttp2_nv *nva, int start, http2_header *nva_copy, int end);
|
||||
/*static int http2_parse_host(char *url, char *host, size_t maxHostLen);*/
|
||||
|
||||
static http2_ping_state_t ping_state = PING_IDLE;
|
||||
|
||||
int g_recv_timeout = 10;
|
||||
|
||||
int set_http2_recv_timeout(int timeout)
|
||||
{
|
||||
g_recv_timeout = timeout;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t send_callback(nghttp2_session *session, const uint8_t *data,
|
||||
size_t length, int flags, void *user_data)
|
||||
{
|
||||
http2_connection_t *connection;
|
||||
httpclient_t *client;
|
||||
int rv;
|
||||
|
||||
connection = (http2_connection_t *)user_data;
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NGHTTP2_DBG("send_callback data len %d, session->remote_window_size=%d!\r\n", (int)length,
|
||||
session->remote_window_size);
|
||||
if (session->remote_window_size < length * 2) {
|
||||
HAL_SleepMs(50);
|
||||
NGHTTP2_DBG("wait a munite ....");
|
||||
}
|
||||
/*if(length < 50)
|
||||
LITE_hexdump("data:", data, length);*/
|
||||
client = (httpclient_t *)connection->network;
|
||||
rv = client->net.write(&client->net, (char *)data, length, 5000);
|
||||
NGHTTP2_DBG("send_callback data ends len = %d!\r\n", rv);
|
||||
if (rv < 0) {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief The implementation of nghttp2_recv_callback type. Here we read |data| from the network
|
||||
* and write them in |buf|. The capacity of |buf| is |length| bytes. Returns the number of
|
||||
* bytes stored in |buf|. See the documentation of nghttp2_recv_callback for the details.
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_on_frame_send_callback()`.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] buf: receive data buffer.
|
||||
* @param[in] length: data length.
|
||||
* @param[in] flags: no using.
|
||||
* @param[in] user_data: user data.
|
||||
* @return Received data length.
|
||||
*/
|
||||
static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf,
|
||||
size_t length, int flags, void *user_data)
|
||||
{
|
||||
http2_connection_t *connection;
|
||||
int rv;
|
||||
httpclient_t *client;
|
||||
|
||||
connection = (http2_connection_t *)user_data;
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
client = (httpclient_t *)connection->network;
|
||||
|
||||
rv = client->net.read(&client->net, (char *)buf, length, g_recv_timeout);
|
||||
/* NGHTTP2_DBG("recv_callback len= %d\r\n", rv); */
|
||||
if (rv < 0) {
|
||||
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
} else if (rv == 0) {
|
||||
rv = 0;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
/**
|
||||
* @brief Callback function invoked after the frame |frame| is sent.
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use
|
||||
* `nghttp2_session_callbacks_set_on_frame_send_callback()`.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] frame: nghttp2 frame.
|
||||
* @param[in] user_data: The |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`
|
||||
* @return The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()`
|
||||
* and `nghttp2_session_mem_send()` functions immediately return :enum:
|
||||
* `NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
static int on_frame_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS: {
|
||||
const nghttp2_nv *nva = frame->headers.nva;
|
||||
NGHTTP2_DBG("[INFO] C ---------> S (HEADERS) stream_id [%d]\n", frame->hd.stream_id);
|
||||
for (i = 0; i < frame->headers.nvlen; ++i) {
|
||||
NGHTTP2_DBG("> %s: %s\n", nva[i].name, nva[i].value);
|
||||
}
|
||||
(void)nva;
|
||||
} break;
|
||||
case NGHTTP2_RST_STREAM: {
|
||||
NGHTTP2_DBG("[INFO] C ---------> S (RST_STREAM)\n");
|
||||
} break;
|
||||
case NGHTTP2_GOAWAY: {
|
||||
NGHTTP2_DBG("[INFO] C ---------> S (GOAWAY) code = %d\n",frame->goaway.error_code);
|
||||
} break;
|
||||
case NGHTTP2_PING: {
|
||||
NGHTTP2_DBG("[INFO] C ---------> S (PING)\n");
|
||||
ping_state = PING_SENDING;
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (connection->cbs && connection->cbs->on_user_frame_send_cb) {
|
||||
connection->cbs->on_user_frame_send_cb(frame->hd.stream_id, frame->hd.type, frame->hd.flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function invoked by `nghttp2_session_recv()` and `nghttp2_session_mem_recv()` when a frame is received.
|
||||
* If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen``member of their data structure are always
|
||||
* ``NULL`` and 0 respectively. The header name/value pairs are emitted via:type:`nghttp2_on_header_callback`
|
||||
* To set this callback to :type:`nghttp2_session_callbacks`, use`nghttp2_session_callbacks_set_on_frame_send_callback()`.
|
||||
* For HEADERS, PUSH_PROMISE and DATA frames, this callback may be called after stream is closed (see:type:
|
||||
* `nghttp2_on_stream_close_callback`). The application should check that stream is still alive using its own stream
|
||||
* management or :func:`nghttp2_session_get_stream_user_data()`.
|
||||
* Only HEADERS and DATA frame can signal the end of incoming data. If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM``
|
||||
* is nonzero, the|frame| is the last frame from the remote peer in this stream.
|
||||
* This callback won't be called for CONTINUATION frames.
|
||||
* HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] frame: nghttp2 frame.
|
||||
* @param[in] user_data: The |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`
|
||||
* @return The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()`
|
||||
* and `nghttp2_session_mem_send()` functions immediately return :enum:
|
||||
* `NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
static int on_frame_recv_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
http2_request *req;
|
||||
NGHTTP2_DBG("on_frame_recv_callback, type = %d\n", frame->hd.type);
|
||||
NGHTTP2_DBG("on_frame_recv_callback, stream_id = %d\n", frame->hd.stream_id);
|
||||
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
req = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
|
||||
if (req == NULL) {
|
||||
NGHTTP2_DBG("stream user data is not exist\n");
|
||||
}
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS: {
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
|
||||
}
|
||||
} break;
|
||||
case NGHTTP2_RST_STREAM: {
|
||||
connection->status = 0;
|
||||
NGHTTP2_DBG("[INFO] C <--------- S (RST_STREAM)\n");
|
||||
} break;
|
||||
case NGHTTP2_GOAWAY: {
|
||||
connection->status = 0;
|
||||
NGHTTP2_DBG("[INFO] C <--------- S (GOAWAY) code = %d\n",frame->goaway.error_code);
|
||||
} break;
|
||||
case NGHTTP2_DATA: {
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
NGHTTP2_DBG("end stream flag\r\n");
|
||||
}
|
||||
} break;
|
||||
case NGHTTP2_PING: {
|
||||
NGHTTP2_DBG("[INFO] C <--------- S (PING)\n");
|
||||
ping_state = PING_RECVED;
|
||||
} break;
|
||||
}
|
||||
|
||||
if (connection->cbs && connection->cbs->on_user_frame_recv_cb) {
|
||||
connection->cbs->on_user_frame_recv_cb(frame->hd.stream_id, frame->hd.type, frame->hd.flags);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback function invoked when the stream |stream_id| is closed.
|
||||
* We use this function to know if the response is fully received. Since we just fetch 1 resource in this program, after
|
||||
* the response is received, we submit GOAWAY and close the session.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] stream_id: stream id.
|
||||
* @param[in] error_code: The reason of closure.
|
||||
* Usually one of :enum:`nghttp2_error_code`, but that is not guaranteed. The stream_user_data, which was specified in
|
||||
* `nghttp2_submit_request()` or `nghttp2_submit_headers()`, is still available in this function.
|
||||
* @param[in] user_data: The |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`
|
||||
* @return The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()`
|
||||
* and `nghttp2_session_mem_send()` functions immediately return :enum:
|
||||
* `NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
static int on_h2_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code,
|
||||
void *user_data)
|
||||
{
|
||||
http2_request *req;
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if (req) {
|
||||
int rv;
|
||||
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
|
||||
|
||||
if (rv != 0) {
|
||||
NGHTTP2_DBG("stream close nghttp2_session_terminate_session\r\n");
|
||||
}
|
||||
}
|
||||
if (connection->cbs && connection->cbs->on_user_stream_close_cb) {
|
||||
connection->cbs->on_user_stream_close_cb(stream_id, error_code);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function invoked when a chunk of data in DATA frame is received.
|
||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We use this function to print the received response body.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] flags: no using.
|
||||
* @param[in] stream_id: the stream ID this DATA frame belongs to.
|
||||
* @param[in] data: receive data.
|
||||
* @param[in] len: data length.
|
||||
* @param[in] user_data: The |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`
|
||||
* @return The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()`
|
||||
* and `nghttp2_session_mem_send()` functions immediately return :enum:
|
||||
* `NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
static int on_data_chunk_recv_callback(nghttp2_session *session,
|
||||
uint8_t flags, int32_t stream_id,
|
||||
const uint8_t *data, size_t len,
|
||||
void *user_data)
|
||||
{
|
||||
http2_request *req;
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
req = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if (req) {
|
||||
NGHTTP2_DBG("stream user data is not exist\n");
|
||||
}
|
||||
NGHTTP2_DBG("[INFO] C <----------- S (DATA chunk) stream_id [%d] :: %lu bytes\n", stream_id, (unsigned long int)len);
|
||||
|
||||
if (connection->cbs && connection->cbs->on_user_chunk_recv_cb) {
|
||||
connection->cbs->on_user_chunk_recv_cb(stream_id, data, len, flags);
|
||||
}
|
||||
|
||||
nghttp2_session_consume_connection(session, len);
|
||||
nghttp2_session_consume(session, stream_id, len);
|
||||
nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, 0, len);
|
||||
nghttp2_submit_window_update(session, NGHTTP2_FLAG_NONE, stream_id, len);
|
||||
nghttp2_session_send(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Callback function invoked when a header name/value pair is received.
|
||||
* The implementation of nghttp2_on_data_chunk_recv_callback type. We use this function to print the received response body.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] frame: nghttp2 frame.
|
||||
* @param[in] name: header name.
|
||||
* @param[in] namelen: length of header name.
|
||||
* @param[in] value: header value.
|
||||
* @param[in] valuelen: length of header value.
|
||||
* @param[in] flags: no using.
|
||||
* @param[in] user_data: The |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`
|
||||
* @return The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()`
|
||||
* and `nghttp2_session_mem_send()` functions immediately return :enum:
|
||||
* `NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
|
||||
|
||||
static int on_header_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, const uint8_t *name,
|
||||
size_t namelen, const uint8_t *value,
|
||||
size_t valuelen, uint8_t flags,
|
||||
void *user_data)
|
||||
{
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
/* Print response headers for the initiated request. */
|
||||
NGHTTP2_DBG("< %s: %s\n", name, value);
|
||||
|
||||
if (connection->cbs && connection->cbs->on_user_header_cb) {
|
||||
connection->cbs->on_user_header_cb(frame->hd.stream_id, (int)frame->headers.cat, name, namelen, value, valuelen, flags);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when nghttp2 library gets started to receive header block.
|
||||
* @param[in] session: nghttp2 session.
|
||||
* @param[in] frame: nghttp2 frame.
|
||||
* @param[in] user_data: The |user_data| pointer is the third argument passed in to the call to
|
||||
* `nghttp2_session_client_new()` or `nghttp2_session_server_new()`
|
||||
* @return The implementation of this function must return 0 if it succeeds.
|
||||
* If nonzero is returned, it is treated as fatal error and `nghttp2_session_send()`
|
||||
* and `nghttp2_session_mem_send()` functions immediately return :enum:
|
||||
* `NGHTTP2_ERR_CALLBACK_FAILURE`.
|
||||
*/
|
||||
static int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame,
|
||||
void *user_data)
|
||||
{
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS:
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
|
||||
NGHTTP2_DBG("[INFO] C <--------- S (HEADERS) stream_id [%d]\n", (int)frame->hd.stream_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Setup callback functions.
|
||||
* nghttp2 API offers many callback functions, but most of them are optional. The send_callback is always required.
|
||||
* Since we use nghttp2_session_recv(), the recv_callback is also required.
|
||||
* @param[in|out] callbacks: nghttp2 callbacks.
|
||||
* @return None.
|
||||
*/
|
||||
static void setup_nghttp2_callbacks(nghttp2_session_callbacks *callbacks)
|
||||
{
|
||||
nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_recv_callback(callbacks, recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_send_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_h2_stream_close_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static ssize_t data_read_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint8_t *buf, size_t length,
|
||||
uint32_t *data_flags,
|
||||
nghttp2_data_source *source,
|
||||
void *user_data)
|
||||
{
|
||||
int len = 0;
|
||||
http2_connection_t *connection = (http2_connection_t *)user_data;
|
||||
if (connection == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (source->ptr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (connection != NULL && connection->flag != NGHTTP2_FLAG_END_STREAM) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
|
||||
connection->flag = NGHTTP2_FLAG_NONE;
|
||||
}
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
/*len = strlen((char *)source->ptr);*/
|
||||
len = source->len;
|
||||
|
||||
if (length < len) {
|
||||
len = length;
|
||||
}
|
||||
memcpy(buf, source->ptr, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static int http2_nv_copy_nghttp2_nv(nghttp2_nv *nva, int start, http2_header *nva_copy, int end)
|
||||
{
|
||||
int i, j;
|
||||
for (i = start, j = 0; j < end; i++, j++) {
|
||||
nva[i].flags = NGHTTP2_NV_FLAG_NONE;
|
||||
nva[i].name = (uint8_t *)nva_copy[j].name;
|
||||
nva[i].value = (uint8_t *)nva_copy[j].value;
|
||||
nva[i].namelen = nva_copy[j].namelen;
|
||||
nva[i].valuelen = nva_copy[j].valuelen;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Connect the SSL client.
|
||||
* @param[in] pclient: http client.
|
||||
* @param[in] url. destination url.
|
||||
* @param[in] port. destination port.
|
||||
* @param[in] ssl_config: custome config.
|
||||
* @return The result. 0 is ok.
|
||||
*/
|
||||
static int http2_client_conn(httpclient_t *pclient, char *url, int port)
|
||||
{
|
||||
int ret = 0;
|
||||
/*char host[MAX_HTTP2_HOST_LEN] = { 0 };*/
|
||||
|
||||
/*http2_parse_host(url, host, sizeof(host));*/
|
||||
if (0 == pclient->net.handle) {
|
||||
/* Establish connection if no. */
|
||||
extern const char *iotx_ca_crt;
|
||||
|
||||
ret = iotx_net_init(&pclient->net, url, port, iotx_ca_crt);
|
||||
|
||||
if (0 != ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = httpclient_connect(pclient);
|
||||
if (0 != ret) {
|
||||
h2_err("http2client_connect is error, ret = %d", ret);
|
||||
httpclient_close(pclient);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iotx_http2_client_send(http2_connection_t *conn, http2_data *h2_data)
|
||||
{
|
||||
int send_flag = 0;
|
||||
int rv = 0;
|
||||
nghttp2_data_provider data_prd;
|
||||
nghttp2_nv *nva = NULL;
|
||||
int nva_size = 0;
|
||||
http2_header *header = h2_data->header;
|
||||
int header_count = h2_data->header_count;
|
||||
char *data = h2_data->data;
|
||||
int len = h2_data->len;
|
||||
int stream_id = h2_data->stream_id;
|
||||
int flags = h2_data->flag;
|
||||
|
||||
if (conn == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header != NULL && header_count != 0) {
|
||||
nva = (nghttp2_nv *)HTTP2_STREAM_MALLOC(sizeof(nghttp2_nv) * header_count);
|
||||
if (nva == NULL) {
|
||||
return -1;
|
||||
}
|
||||
nva_size = http2_nv_copy_nghttp2_nv(nva, nva_size, header, header_count);
|
||||
}
|
||||
/*upload to server*/
|
||||
if (data != NULL && len != 0) {
|
||||
data_prd.source.ptr = data;
|
||||
data_prd.source.len = len;
|
||||
data_prd.read_callback = data_read_callback;
|
||||
if (nva_size != 0) {
|
||||
rv = nghttp2_submit_request(conn->session, NULL, nva, nva_size, &data_prd, NULL);
|
||||
h2_data->stream_id = rv;
|
||||
} else {
|
||||
rv = nghttp2_submit_data(conn->session, flags, stream_id, &data_prd);
|
||||
}
|
||||
} else {
|
||||
rv = nghttp2_submit_request(conn->session, NULL, nva, nva_size, NULL, NULL);
|
||||
h2_data->stream_id = rv;
|
||||
}
|
||||
HTTP2_STREAM_FREE(nva);
|
||||
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
send_flag = nghttp2_session_want_write(conn->session);
|
||||
if (send_flag) {
|
||||
rv = nghttp2_session_send(conn->session);
|
||||
NGHTTP2_DBG("nghttp2_session_send %d\r\n", rv);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int iotx_http2_client_recv(http2_connection_t *conn, char *data, int data_len, int *len, int timeout)
|
||||
{
|
||||
int rv = 0;
|
||||
int read_flag = 0;
|
||||
|
||||
if (conn == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
set_http2_recv_timeout(timeout);
|
||||
read_flag = nghttp2_session_want_read(conn->session);
|
||||
if (read_flag) {
|
||||
rv = nghttp2_session_recv(conn->session);
|
||||
NGHTTP2_DBG("nghttp2_client_recv %d\r\n", rv);
|
||||
if (rv < 0) {
|
||||
read_flag = 0;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the http2 client connect.
|
||||
* @param[in] pclient: http client.
|
||||
* @return http2 client connection handler.
|
||||
*/
|
||||
http2_connection_t *iotx_http2_client_connect(void *pclient, char *url, int port)
|
||||
{
|
||||
http2_connection_t *connection;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
int rv;
|
||||
int ret = 0;
|
||||
|
||||
connection = HTTP2_STREAM_MALLOC(sizeof(http2_connection_t));
|
||||
if (connection == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(connection, 0, sizeof(http2_connection_t));
|
||||
|
||||
if (0 != (ret = http2_client_conn((httpclient_t *)pclient, url, port))) {
|
||||
NGHTTP2_DBG("https_client_conn failed %d\r\n", ret);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
connection->network = pclient;
|
||||
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
if (rv != 0) {
|
||||
NGHTTP2_DBG("nghttp2_session_callbacks_new1 %d", rv);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
setup_nghttp2_callbacks(callbacks);
|
||||
rv = nghttp2_session_client_new((nghttp2_session **)&connection->session, callbacks, connection);
|
||||
if (rv != 0) {
|
||||
NGHTTP2_DBG("nghttp2_session_client_new3 %d", rv);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
nghttp2_submit_settings(connection->session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||
#if 0
|
||||
|
||||
parse_uri(&uri, url);
|
||||
request_init(&req, &uri);
|
||||
/* Submit the HTTP request to the outbound queue. */
|
||||
submit_request(connection, &req);
|
||||
#endif
|
||||
|
||||
rv = nghttp2_session_send(connection->session);
|
||||
/*request_free(&req);*/
|
||||
if (rv < 0) {
|
||||
NGHTTP2_DBG("nghttp2_session_send fail %d", rv);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
connection->status = 1;
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the http2 client connect.
|
||||
* @param[in] pclient: http client.
|
||||
* @return http2 client connection handler.
|
||||
*/
|
||||
http2_connection_t *iotx_http2_client_connect_with_cb(void *pclient, char *url, int port, http2_user_cb_t *cb)
|
||||
{
|
||||
http2_connection_t *connection;
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
int rv;
|
||||
int ret = 0;
|
||||
|
||||
connection = HTTP2_STREAM_MALLOC(sizeof(http2_connection_t));
|
||||
if (connection == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(connection, 0, sizeof(http2_connection_t));
|
||||
|
||||
if (0 != (ret = http2_client_conn((httpclient_t *)pclient, url, port))) {
|
||||
NGHTTP2_DBG("https_client_conn failed %d\r\n", ret);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
connection->network = pclient;
|
||||
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
if (rv != 0) {
|
||||
NGHTTP2_DBG("nghttp2_session_callbacks_new1 %d", rv);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
connection->cbs = cb;
|
||||
setup_nghttp2_callbacks(callbacks);
|
||||
|
||||
rv = nghttp2_session_client_new((nghttp2_session **)&connection->session, callbacks, connection);
|
||||
if (rv != 0) {
|
||||
NGHTTP2_DBG("nghttp2_session_client_new3 %d", rv);
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
nghttp2_session_callbacks_del(callbacks);
|
||||
|
||||
nghttp2_submit_settings(connection->session, NGHTTP2_FLAG_NONE, NULL, 0);
|
||||
#if 0
|
||||
|
||||
parse_uri(&uri, url);
|
||||
request_init(&req, &uri);
|
||||
/* Submit the HTTP request to the outbound queue. */
|
||||
submit_request(connection, &req);
|
||||
#endif
|
||||
|
||||
rv = nghttp2_session_send(connection->session);
|
||||
/*request_free(&req);*/
|
||||
if (rv < 0) {
|
||||
nghttp2_session_del(connection->session);
|
||||
NGHTTP2_DBG("nghttp2_session_send fail %d", rv);
|
||||
HTTP2_STREAM_FREE(connection);
|
||||
return NULL;
|
||||
}
|
||||
connection->status = 1;
|
||||
return connection;
|
||||
}
|
||||
|
||||
int iotx_http2_client_disconnect(http2_connection_t *conn)
|
||||
{
|
||||
/* Resource cleanup */
|
||||
if (conn == NULL) {
|
||||
return -1;
|
||||
}
|
||||
httpclient_close((httpclient_t *)conn->network);
|
||||
nghttp2_session_del(conn->session);
|
||||
HTTP2_STREAM_FREE(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iotx_http2_client_send_ping(http2_connection_t *conn)
|
||||
{
|
||||
int rv = 0;
|
||||
int send_flag;
|
||||
|
||||
ping_state = PING_IDLE;
|
||||
|
||||
if (conn == NULL) {
|
||||
return -1;
|
||||
}
|
||||
rv = nghttp2_submit_ping(conn->session, NGHTTP2_FLAG_NONE, NULL);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
send_flag = nghttp2_session_want_write(conn->session);
|
||||
if (send_flag) {
|
||||
rv = nghttp2_session_send(conn->session);
|
||||
NGHTTP2_DBG("nghttp2_session_send %d\r\n", rv);
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
ping_state = PING_SENDING;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iotx_http2_client_recv_ping(void)
|
||||
{
|
||||
if (ping_state == PING_RECVED || ping_state == PING_IDLE) {
|
||||
NGHTTP2_DBG("ping recv secceed\r\n");
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
NGHTTP2_DBG("ping recv timeout");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int iotx_http2_get_available_window_size(http2_connection_t *conn)
|
||||
{
|
||||
int windows_size = 0;
|
||||
|
||||
if (conn == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
windows_size = nghttp2_session_get_remote_window_size(conn->session);
|
||||
return windows_size;
|
||||
}
|
||||
|
||||
|
||||
int iotx_http2_update_window_size(http2_connection_t *conn)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (conn == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rv = nghttp2_session_recv(conn->session);
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs the network I/O.
|
||||
*/
|
||||
int iotx_http2_exec_io(http2_connection_t *connection)
|
||||
{
|
||||
if (connection == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nghttp2_session_want_read(connection->session) /*||
|
||||
nghttp2_session_want_write(connection->session)*/) {
|
||||
|
||||
int rv;
|
||||
rv = nghttp2_session_recv(connection->session);
|
||||
if (rv < 0) {
|
||||
NGHTTP2_DBG("nghttp2_session_recv error");
|
||||
return -1;
|
||||
}
|
||||
/* rv = nghttp2_session_send(connection->session); */
|
||||
/* if (rv < 0) { */
|
||||
/* NGHTTP2_DBG("nghttp2_session_send error"); */
|
||||
/* return -1; */
|
||||
/* } */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iotx_http2_reset_stream(http2_connection_t *connection, int32_t stream_id)
|
||||
{
|
||||
int rv = 0;
|
||||
if(connection == NULL){
|
||||
return -1;
|
||||
}
|
||||
if(!nghttp2_session_get_stream_local_close(connection->session,stream_id)) {
|
||||
rv = nghttp2_submit_rst_stream(connection->session,0, stream_id, NGHTTP2_NO_ERROR);
|
||||
}
|
||||
if (rv < 0) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = nghttp2_session_want_write(connection->session);
|
||||
if (rv) {
|
||||
rv = nghttp2_session_send(connection->session);
|
||||
NGHTTP2_DBG("nghttp2_session_send %d\r\n", rv);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
Reference in New Issue
Block a user