9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
1002 lines
29 KiB
C
1002 lines
29 KiB
C
/*
|
|
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
|
|
*/
|
|
#include "infra_config.h"
|
|
|
|
#ifdef INFRA_HTTPC
|
|
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "infra_state.h"
|
|
#include "infra_compat.h"
|
|
#include "infra_types.h"
|
|
#include "infra_defs.h"
|
|
#include "infra_httpc.h"
|
|
#include "infra_net.h"
|
|
#include "infra_timer.h"
|
|
#include "wrappers.h"
|
|
#include "iotx_ota_internal.h"
|
|
#include "usart.h"
|
|
#include "dm_ota.h" //TODO lyn
|
|
|
|
#ifdef INFRA_LOG
|
|
#include "infra_log.h"
|
|
#define httpc_err(...) log_err("httpc", __VA_ARGS__)
|
|
#define httpc_info(...) log_info("httpc", __VA_ARGS__)
|
|
#define httpc_debug(...) log_debug("httpc", __VA_ARGS__)
|
|
#else
|
|
#define httpc_err(...)
|
|
#define httpc_info(...)
|
|
#define httpc_debug(...)
|
|
#endif
|
|
|
|
#define HTTPCLIENT_MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
#define HTTPCLIENT_MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
|
|
#define HTTPCLIENT_READ_BUF_SIZE (1024) /* read payload */
|
|
#define HTTPCLIENT_RAED_HEAD_SIZE (32) /* read header */
|
|
#define HTTPCLIENT_SEND_BUF_SIZE (1024) /* send */
|
|
|
|
#define HTTPCLIENT_MAX_URL_LEN (256)
|
|
|
|
#define HTTP_RETRIEVE_MORE_DATA (1) /**< More data needs to be retrieved. */
|
|
|
|
#if defined(MBEDTLS_DEBUG_C)
|
|
#define DEBUG_LEVEL 2
|
|
#endif
|
|
#define HTTPCLIENT_CHUNK_SIZE (1024)
|
|
|
|
static int _utils_parse_url(const char *url, char *host, char *path);
|
|
static int _http_recv(httpclient_t *client, char *buf, int max_len, int *p_read_len,
|
|
uint32_t timeout);
|
|
static int _http_get_response_body(httpclient_t *client, char *data, int len, uint32_t timeout,
|
|
httpclient_data_t *client_data);
|
|
static int _http_parse_response_header(httpclient_t *client, char *data, int len, uint32_t timeout,
|
|
httpclient_data_t *client_data);
|
|
|
|
static int _utils_parse_url(const char *url, char *host,
|
|
char *path)
|
|
{
|
|
char *host_ptr = (char *)strstr(url, "://");
|
|
uint32_t host_len = 0;
|
|
uint32_t path_len;
|
|
/* char *port_ptr; */
|
|
char *path_ptr;
|
|
char *fragment_ptr;
|
|
|
|
if (host_ptr == NULL)
|
|
{
|
|
return STATE_USER_INPUT_HTTP_URL; /* URL is invalid */
|
|
}
|
|
host_ptr += 3;
|
|
|
|
path_ptr = strchr(host_ptr, '/');
|
|
if (NULL == path_ptr)
|
|
{
|
|
return STATE_USER_INPUT_HTTP_PATH;
|
|
}
|
|
|
|
if (host_len == 0)
|
|
{
|
|
host_len = path_ptr - host_ptr;
|
|
}
|
|
|
|
memcpy(host, host_ptr, host_len);
|
|
host[host_len] = '\0';
|
|
fragment_ptr = strchr(host_ptr, '#');
|
|
if (fragment_ptr != NULL)
|
|
{
|
|
path_len = fragment_ptr - path_ptr;
|
|
}
|
|
else
|
|
{
|
|
path_len = strlen(path_ptr);
|
|
}
|
|
|
|
memcpy(path, path_ptr, path_len);
|
|
path[path_len] = '\0';
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
static int _utils_fill_tx_buffer(httpclient_t *client, char *send_buf, int *send_idx, char *buf,
|
|
uint32_t len) /* 0 on success, err code on failure */
|
|
{
|
|
int ret;
|
|
int cp_len;
|
|
int idx = *send_idx;
|
|
|
|
if (len == 0)
|
|
{
|
|
len = strlen(buf);
|
|
}
|
|
do
|
|
{
|
|
if ((HTTPCLIENT_SEND_BUF_SIZE - idx) >= len)
|
|
{
|
|
cp_len = len;
|
|
}
|
|
else
|
|
{
|
|
cp_len = HTTPCLIENT_SEND_BUF_SIZE - idx;
|
|
}
|
|
|
|
memcpy(send_buf + idx, buf, cp_len);
|
|
idx += cp_len;
|
|
len -= cp_len;
|
|
|
|
if (idx == HTTPCLIENT_SEND_BUF_SIZE)
|
|
{
|
|
ret = client->net.write(&client->net, send_buf, HTTPCLIENT_SEND_BUF_SIZE, 5000);
|
|
if (ret)
|
|
{
|
|
return (ret);
|
|
}
|
|
}
|
|
} while (len);
|
|
|
|
*send_idx = idx;
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
static int _http_send_header(httpclient_t *client, const char *host, const char *path, int method,
|
|
httpclient_data_t *client_data)
|
|
{
|
|
int len;
|
|
char send_buf[HTTPCLIENT_SEND_BUF_SIZE] = {0};
|
|
char buf[HTTPCLIENT_SEND_BUF_SIZE] = {0};
|
|
char *meth = (method == HTTPCLIENT_GET) ? "GET" : (method == HTTPCLIENT_POST) ? "POST"
|
|
: (method == HTTPCLIENT_PUT) ? "PUT"
|
|
: (method == HTTPCLIENT_DELETE) ? "DELETE"
|
|
: (method == HTTPCLIENT_HEAD) ? "HEAD"
|
|
: "";
|
|
int ret;
|
|
|
|
/* Send request */
|
|
memset(send_buf, 0, HTTPCLIENT_SEND_BUF_SIZE);
|
|
len = 0; /* Reset send buffer */
|
|
|
|
HAL_Snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); /* Write request */
|
|
|
|
ret = _utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
|
|
if (ret)
|
|
{
|
|
/* httpc_err("Could not write request"); */
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
|
|
/* Add user header information */
|
|
if (client->header)
|
|
{
|
|
_utils_fill_tx_buffer(client, send_buf, &len, (char *)client->header, strlen(client->header));
|
|
}
|
|
|
|
if (client_data->post_buf != NULL)
|
|
{
|
|
HAL_Snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", client_data->post_buf_len);
|
|
_utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
|
|
|
|
if (client_data->post_content_type != NULL)
|
|
{
|
|
HAL_Snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", client_data->post_content_type);
|
|
_utils_fill_tx_buffer(client, send_buf, &len, buf, strlen(buf));
|
|
}
|
|
}
|
|
|
|
/* Close headers */
|
|
_utils_fill_tx_buffer(client, send_buf, &len, "\r\n", 0);
|
|
|
|
#ifdef INFRA_LOG
|
|
log_multi_line(LOG_DEBUG_LEVEL, "REQUEST", "%s", send_buf, ">");
|
|
#endif
|
|
|
|
/* ret = httpclient_tcp_send_all(client->net.handle, send_buf, len); */
|
|
ret = client->net.write(&client->net, send_buf, len, 5000);
|
|
if (ret <= 0)
|
|
{
|
|
httpc_err("ret = client->net.write() = %d", ret);
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
int _http_send_userdata(httpclient_t *client, httpclient_data_t *client_data)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (client_data->post_buf && client_data->post_buf_len)
|
|
{
|
|
/* ret = httpclient_tcp_send_all(client->handle, (char *)client_data->post_buf, client_data->post_buf_len); */
|
|
ret = client->net.write(&client->net, (char *)client_data->post_buf, client_data->post_buf_len, 5000);
|
|
httpc_debug("client_data->post_buf: %s, ret is %d", client_data->post_buf, ret);
|
|
if (ret <= 0)
|
|
{
|
|
return STATE_SYS_DEPEND_NWK_CLOSE; /* Connection was closed by server */
|
|
}
|
|
}
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
/* 0 on success, err code on failure */
|
|
static int _http_recv(httpclient_t *client, char *buf, int max_len, int *p_read_len,
|
|
uint32_t timeout_ms)
|
|
{
|
|
int ret = 0;
|
|
iotx_time_t timer;
|
|
|
|
iotx_time_init(&timer);
|
|
utils_time_countdown_ms(&timer, timeout_ms);
|
|
|
|
*p_read_len = 0;
|
|
|
|
ret = client->net.read(&client->net, buf, max_len, iotx_time_left(&timer));
|
|
/* httpc_debug("Recv: | %s", buf); */
|
|
httpc_info("ret of _http_recv is %d", ret);
|
|
|
|
if (ret > 0)
|
|
{
|
|
*p_read_len = ret;
|
|
return STATE_SUCCESS;
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
/* timeout */
|
|
return STATE_SYS_DEPEND_NWK_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
}
|
|
|
|
#define MIN_TIMEOUT (100)
|
|
#define MAX_RETRY_COUNT (600)
|
|
|
|
static int _utils_check_deadloop(int len, iotx_time_t *timer, int ret, unsigned int *dead_loop_count,
|
|
unsigned int *extend_count)
|
|
{
|
|
/* if timeout reduce to zero, it will be translated into NULL for select function in TLS lib */
|
|
/* it would lead to indenfinite behavior, so we avoid it */
|
|
if (iotx_time_left(timer) < MIN_TIMEOUT)
|
|
{
|
|
(*extend_count)++;
|
|
utils_time_countdown_ms(timer, MIN_TIMEOUT);
|
|
}
|
|
|
|
/* if it falls into deadloop before reconnected to internet, we just quit*/
|
|
if ((0 == len) && (0 == iotx_time_left(timer)) && (STATE_SUCCESS > ret))
|
|
{
|
|
(*dead_loop_count)++;
|
|
if (*dead_loop_count > MAX_RETRY_COUNT)
|
|
{
|
|
httpc_err("deadloop detected, exit");
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*dead_loop_count = 0;
|
|
}
|
|
|
|
/*if the internet connection is fixed during the loop, the download stream might be disconnected. we have to quit */
|
|
if ((0 == len) && (*extend_count > 2 * MAX_RETRY_COUNT) && (STATE_SUCCESS > ret))
|
|
{
|
|
httpc_err("extend timer for too many times, exit");
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
static int _utils_fill_rx_buf(int *recv_count, int len_to_write_to_respons_buf, httpclient_data_t *client_data,
|
|
char *data)
|
|
{
|
|
int count = *recv_count;
|
|
//if (count + len_to_write_to_respons_buf < client_data->response_buf_len - 1)
|
|
if (count + len_to_write_to_respons_buf <= client_data->response_buf_len)
|
|
{
|
|
memcpy(client_data->response_buf + count, data, len_to_write_to_respons_buf);
|
|
count += len_to_write_to_respons_buf;
|
|
//client_data->response_buf[count] = '\0';
|
|
client_data->retrieve_len -= len_to_write_to_respons_buf;
|
|
*recv_count = count;
|
|
return STATE_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
memcpy(client_data->response_buf + count, data, client_data->response_buf_len - 1 - count);
|
|
client_data->response_buf[client_data->response_buf_len - 1] = '\0';
|
|
client_data->retrieve_len -= (client_data->response_buf_len - 1 - count);
|
|
return HTTP_RETRIEVE_MORE_DATA;
|
|
}
|
|
}
|
|
|
|
static int _http_get_response_body(httpclient_t *client, char *data, int data_len_actually_received,
|
|
uint32_t timeout_ms, httpclient_data_t *client_data)
|
|
{
|
|
int written_response_buf_len = 0;
|
|
int len_to_write_to_respons_buf = 0;
|
|
iotx_time_t timer;
|
|
|
|
iotx_time_init(&timer);
|
|
utils_time_countdown_ms(&timer, timeout_ms);
|
|
|
|
/* Receive data */
|
|
/* httpc_debug("Current data: %s", data); */
|
|
|
|
client_data->is_more = IOT_TRUE;
|
|
|
|
/* the header is not received finished */
|
|
if (client_data->response_content_len == -1 && client_data->is_chunked == IOT_FALSE)
|
|
{
|
|
/* can not enter this if */
|
|
/* TODO check the way to go into this branch */
|
|
httpc_err("header is not received yet");
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
unsigned int dead_loop_count = 0;
|
|
unsigned int extend_count = 0;
|
|
do
|
|
{
|
|
int res;
|
|
/* move previous fetched data into response_buf */
|
|
len_to_write_to_respons_buf = HTTPCLIENT_MIN(data_len_actually_received, client_data->retrieve_len);
|
|
res = _utils_fill_rx_buf(&written_response_buf_len, len_to_write_to_respons_buf, client_data, data);
|
|
if (HTTP_RETRIEVE_MORE_DATA == res)
|
|
{
|
|
return HTTP_RETRIEVE_MORE_DATA;
|
|
}
|
|
|
|
/* get data from internet and put into "data" buf temporary */
|
|
if (client_data->retrieve_len)
|
|
{
|
|
int ret;
|
|
// int max_len_to_receive = HTTPCLIENT_MIN(HTTPCLIENT_CHUNK_SIZE - 1,
|
|
// client_data->response_buf_len - 1 - written_response_buf_len);
|
|
int max_len_to_receive = HTTPCLIENT_MIN(HTTPCLIENT_CHUNK_SIZE - 1,
|
|
client_data->response_buf_len - written_response_buf_len);
|
|
max_len_to_receive = HTTPCLIENT_MIN(max_len_to_receive, client_data->retrieve_len);
|
|
|
|
HAL_Printf("接收主体,预请求字节数=%d[%d, %d]\n", max_len_to_receive, client_data->response_buf_len, written_response_buf_len);
|
|
ret = _http_recv(client, data, max_len_to_receive, &data_len_actually_received, iotx_time_left(&timer));
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
return ret;
|
|
}
|
|
httpc_debug("Total- remaind Payload: %d Bytes; currently Read: %d Bytes", client_data->retrieve_len,
|
|
data_len_actually_received);
|
|
|
|
/* TODO add deadloop processing*/
|
|
ret = _utils_check_deadloop(data_len_actually_received, &timer, ret, &dead_loop_count,
|
|
&extend_count);
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
} while (client_data->retrieve_len);
|
|
HAL_Printf("解析的主体数据完成,is_more = 0\n");
|
|
|
|
client_data->is_more = IOT_FALSE; //
|
|
break;
|
|
}
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
static int _http_parse_response_header(httpclient_t *client, char *data, int len, uint32_t timeout_ms,
|
|
httpclient_data_t *client_data)
|
|
{
|
|
int crlf_pos, res=0;
|
|
iotx_time_t timer;
|
|
char *tmp_ptr, *ptr_body_end;
|
|
int new_trf_len, ret;
|
|
char *crlf_ptr;
|
|
|
|
//TODO lynTest 20220501
|
|
int rangStartLyn = 0;
|
|
void *ota_handle = NULL;
|
|
OTA_Struct_pt h_ota;
|
|
//dm_fota_ctx_t *ctx = dm_fota_get_ctx();
|
|
|
|
iotx_time_init(&timer);
|
|
utils_time_countdown_ms(&timer, timeout_ms);
|
|
|
|
client_data->response_content_len = -1;
|
|
|
|
/* http client response */
|
|
/* <status-line> HTTP/1.1 200 OK(CRLF)
|
|
|
|
<headers> ...(CRLF)
|
|
|
|
<blank line> (CRLF)
|
|
|
|
[<response-body>] */
|
|
// int lenn = strlen(data);
|
|
// logMsg_bs("OTA recv=%d\n",lenn);
|
|
// for(int i=0;i<lenn;i++)
|
|
// logMsg_bs("%c ",data[i]);
|
|
|
|
crlf_ptr = strstr(data, "\r\n");
|
|
if (crlf_ptr == NULL)
|
|
{
|
|
httpc_err("\r\n not found");
|
|
return ERROR_HTTP_UNRESOLVED_DNS;
|
|
}
|
|
|
|
crlf_pos = crlf_ptr - data;
|
|
data[crlf_pos] = '\0';
|
|
client->response_code = atoi(data + 9);
|
|
httpc_debug("Reading headers: %s", data);
|
|
memmove(data, &data[crlf_pos + 2], len - (crlf_pos + 2) + 1); /* Be sure to move NULL-terminating char as well */
|
|
len -= (crlf_pos + 2); /* remove status_line length */
|
|
client_data->is_chunked = IOT_FALSE;
|
|
|
|
/*If not ending of response body*/
|
|
/* try to read more header again until find response head ending "\r\n\r\n" */
|
|
while (NULL == (ptr_body_end = strstr(data, "\r\n\r\n")))
|
|
{
|
|
/* try to read more header */
|
|
int max_remain_len = HTTPCLIENT_READ_BUF_SIZE - len - 1;
|
|
if (max_remain_len <= 0)
|
|
{
|
|
httpc_debug("buffer exceeded max\n");
|
|
return ERROR_HTTP_PARSE;
|
|
}
|
|
max_remain_len = max_remain_len > HTTPCLIENT_RAED_HEAD_SIZE ? HTTPCLIENT_RAED_HEAD_SIZE : max_remain_len;
|
|
ret = _http_recv(client, data + len, max_remain_len, &new_trf_len, iotx_time_left(&timer));
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
return ret;
|
|
}
|
|
len += new_trf_len;
|
|
data[len] = '\0';
|
|
}
|
|
|
|
/* parse response_content_len */
|
|
if (NULL != (tmp_ptr = strstr(data, "Content-Length")))
|
|
{
|
|
client_data->response_content_len = atoi(tmp_ptr + strlen("Content-Length: "));
|
|
client_data->retrieve_len = client_data->response_content_len;
|
|
}
|
|
else
|
|
{
|
|
return STATE_HTTP_RESP_MISSING_CONTENT_LEN;
|
|
}
|
|
|
|
//TODO: 解决接收范围与发送请求范围不匹配的情况 lyn20220501
|
|
if (NULL != (tmp_ptr = strstr(data, "Content-Range")))
|
|
{
|
|
rangStartLyn = atoi(tmp_ptr + strlen("Content-Range: bytes "));
|
|
|
|
res = dm_ota_get_ota_handle(&ota_handle);
|
|
|
|
if (res == SUCCESS_RETURN)
|
|
{
|
|
h_ota = (OTA_Struct_pt)ota_handle;
|
|
if(rangStartLyn == h_ota->size_fetched)
|
|
HAL_Printf("------content range start sucess : %d------\n", rangStartLyn);
|
|
else
|
|
{
|
|
HAL_Printf("------content range start failA(%d %d)!!!------\n", rangStartLyn, h_ota->size_fetched);
|
|
//uart1_RdPtrAdd(client_data->retrieve_len);
|
|
//此时应把串口接收缓存读指针+content length
|
|
return 0xFF; //ERROR_HTTP_UNRESOLVED_DNS; //0xFF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HAL_Printf("------content range start failB(%d %d)!!!------\n", rangStartLyn, h_ota->size_fetched);
|
|
//uart1_RdPtrAdd(client_data->retrieve_len);
|
|
//此时应把串口接收缓存读指针+content length
|
|
return 0xFF; //ERROR_HTTP_UNRESOLVED_DNS; //0xFF;
|
|
}
|
|
}
|
|
|
|
if(client->response_code == 400)
|
|
{
|
|
HAL_Printf("HTTP响应码测试 = 400,测试直接返回,串口读指针增加 LYN\n");
|
|
//uart1_RdPtrAdd(client_data->retrieve_len);
|
|
//此时应把串口接收缓存读指针+content length
|
|
return 0xFF; //ERROR_HTTP_UNRESOLVED_DNS; //0xFF;
|
|
}
|
|
|
|
/* remove header length */
|
|
/* len is Had read body's length */
|
|
/* if client_data->response_content_len != 0, it is know response length */
|
|
/* the remain length is client_data->response_content_len - len */
|
|
//len:已接收主体长度, remain_length = 主体总长度-len
|
|
len = len - (ptr_body_end + 4 - data); //接收数据总长度-(HTTP协议首部结尾索引+4(\r\n\r\n)+缓存buf首地址)
|
|
memmove(data, ptr_body_end + 4, len); // + 1);
|
|
//logMsg_bs("解析完头,解析前已接收主体长度=%d\n", client_data->response_received_len);
|
|
//client_data->response_received_len += len;
|
|
client_data->response_received_len = len;
|
|
HAL_Printf("解析完头,开始解析数据体,已接收主体=%d(+%d)\n", client_data->response_received_len, len);
|
|
return _http_get_response_body(client, data, len, iotx_time_left(&timer), client_data);
|
|
}
|
|
|
|
int httpclient_connect(httpclient_t *client)
|
|
{
|
|
int retry_max = 3;
|
|
int retry_cnt = 1;
|
|
int retry_interval = 1000;
|
|
int rc = -1;
|
|
|
|
do
|
|
{
|
|
client->net.handle = 0;
|
|
httpc_debug("calling TCP or TLS connect HAL for [%d/%d] iteration", retry_cnt, retry_max);
|
|
|
|
rc = client->net.connect(&client->net);
|
|
if (0 != rc)
|
|
{
|
|
client->net.disconnect(&client->net);
|
|
httpc_err("TCP or TLS connect failed, rc = %d", rc);
|
|
HAL_SleepMs(retry_interval);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
httpc_debug("rc = client->net.connect() = %d, success @ [%d/%d] iteration", rc, retry_cnt, retry_max);
|
|
break;
|
|
}
|
|
} while (++retry_cnt <= retry_max);
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
int _http_send_request(httpclient_t *client, const char *host, const char *path, HTTPCLIENT_REQUEST_TYPE method,
|
|
httpclient_data_t *client_data)
|
|
{
|
|
int ret = STATE_SUCCESS;
|
|
|
|
if (0 == client->net.handle)
|
|
{
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
|
|
ret = _http_send_header(client, host, path, method, client_data);
|
|
if (ret != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (method == HTTPCLIENT_POST || method == HTTPCLIENT_PUT)
|
|
{
|
|
ret = _http_send_userdata(client, client_data);
|
|
if (ret < 0)
|
|
{
|
|
ret = ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int httpclient_recv_response(httpclient_t *client, uint32_t timeout_ms, httpclient_data_t *client_data)
|
|
{
|
|
int reclen = 0, ret = STATE_SUCCESS;
|
|
iotx_time_t timer;
|
|
char *buf = HAL_Malloc(HTTPCLIENT_READ_BUF_SIZE);
|
|
|
|
if (buf == NULL)
|
|
{
|
|
return STATE_SYS_DEPEND_MALLOC;
|
|
}
|
|
memset(buf, 0, HTTPCLIENT_READ_BUF_SIZE);
|
|
|
|
iotx_time_init(&timer);
|
|
utils_time_countdown_ms(&timer, timeout_ms);
|
|
|
|
if (0 == client->net.handle)
|
|
{
|
|
httpc_err("not connection have been established");
|
|
HAL_Free(buf);
|
|
return STATE_SYS_DEPEND_NWK_CLOSE;
|
|
}
|
|
|
|
if (client_data->is_more)
|
|
{
|
|
client_data->response_buf[0] = '\0';
|
|
ret = _http_get_response_body(client, buf, reclen, iotx_time_left(&timer), client_data);
|
|
}
|
|
else
|
|
{
|
|
client_data->is_more = 1;
|
|
/* try to read header */
|
|
ret = _http_recv(client, buf, HTTPCLIENT_RAED_HEAD_SIZE, &reclen, iotx_time_left(&timer));
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
HAL_Free(buf);
|
|
return ret;
|
|
}
|
|
|
|
buf[reclen] = '\0';
|
|
|
|
if (reclen)
|
|
{
|
|
#ifdef INFRA_LOG
|
|
log_multi_line(LOG_DEBUG_LEVEL, "RESPONSE", "%s", buf, "<");
|
|
#endif
|
|
|
|
|
|
//解析HTTP_header数据
|
|
ret = _http_parse_response_header(client, buf, reclen, iotx_time_left(&timer), client_data);
|
|
|
|
#if 1
|
|
if(ret == 0xFF)
|
|
//if(ret == ERROR_HTTP_UNRESOLVED_DNS)
|
|
{
|
|
HAL_Printf("<LYN TEST> 重新接收一次头\n");
|
|
HAL_SleepMs(400);
|
|
|
|
ret = _http_recv(client, buf, HTTPCLIENT_RAED_HEAD_SIZE, &reclen, iotx_time_left(&timer));
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
HAL_Free(buf);
|
|
return ret;
|
|
}
|
|
|
|
buf[reclen] = '\0';
|
|
|
|
if (reclen)
|
|
{
|
|
log_multi_line(LOG_DEBUG_LEVEL, "RESPONSE", "%s", buf, "<");
|
|
ret = _http_parse_response_header(client, buf, reclen, iotx_time_left(&timer), client_data);
|
|
//// goto lynTest;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
HAL_Free(buf);
|
|
return ret;
|
|
}
|
|
|
|
void httpclient_close(httpclient_t *client)
|
|
{
|
|
if (client->net.handle > 0)
|
|
{
|
|
client->net.disconnect(&client->net);
|
|
}
|
|
client->net.handle = 0;
|
|
httpc_info("client disconnected");
|
|
}
|
|
char host[HTTPCLIENT_MAX_URL_LEN] = {0};
|
|
char path[HTTPCLIENT_MAX_URL_LEN] = {0};
|
|
static int _http_send(httpclient_t *client, const char *url, int port, const char *ca_crt,
|
|
HTTPCLIENT_REQUEST_TYPE method, httpclient_data_t *client_data)
|
|
{
|
|
int ret;
|
|
|
|
/* First we need to parse the url (http[s]://host[:port][/[path]]) */
|
|
ret = _utils_parse_url(url, host, path);
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (0 == client->net.handle)
|
|
{
|
|
/* Establish connection if no. */
|
|
ret = iotx_net_init(&client->net, host, port, ca_crt);
|
|
if (0 != ret)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = httpclient_connect(client);
|
|
if (0 != ret)
|
|
{
|
|
httpclient_close(client);
|
|
return ret;
|
|
}
|
|
HAL_SleepMs(500); //TODO 给4G模块重启一点时间再发送
|
|
ret = _http_send_request(client, host, path, method, client_data);
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
httpc_err("_http_send_request is error, ret = %d", ret);
|
|
return ret;
|
|
}
|
|
HAL_Printf("发送OTA数据请求\n");
|
|
// HAL_SleepMs(500); //TODO 发送OTA请求后等一会
|
|
}
|
|
else if(client_data->is_more == 0)
|
|
{
|
|
//TODO:test lyn
|
|
HAL_Printf("发送新一阶段的程序申请\n");
|
|
HAL_SleepMs(500);
|
|
ret = _http_send_request(client, host, path, method, client_data);
|
|
if (ret != STATE_SUCCESS)
|
|
{
|
|
httpc_err("_http_send_request is error, ret = %d", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
HAL_TASK_WDT_CLEAR(); //清空看门狗计数
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
int httpclient_common(httpclient_t *client, const char *url, int port, const char *ca_crt,
|
|
HTTPCLIENT_REQUEST_TYPE method, uint32_t timeout_ms, httpclient_data_t *client_data)
|
|
{
|
|
iotx_time_t timer;
|
|
int ret = _http_send(client, url, port, ca_crt, method, client_data);
|
|
if (STATE_SUCCESS != ret)
|
|
{
|
|
httpclient_close(client);
|
|
return ret;
|
|
}
|
|
|
|
iotx_time_init(&timer);
|
|
utils_time_countdown_ms(&timer, timeout_ms);
|
|
|
|
if ((NULL != client_data->response_buf) && (0 != client_data->response_buf_len))
|
|
{
|
|
ret = httpclient_recv_response(client, iotx_time_left(&timer), client_data);
|
|
if (ret < STATE_SUCCESS)
|
|
{
|
|
httpc_err("httpclient_recv_response is error,ret = %d", ret);
|
|
httpclient_close(client);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (!client_data->is_more)
|
|
{
|
|
/* Close the HTTP if no more data. */
|
|
httpc_info("close http channel");
|
|
httpclient_close(client);
|
|
}
|
|
|
|
ret = 0;
|
|
return ret;
|
|
}
|
|
|
|
int iotx_post(httpclient_t *client,
|
|
const char *url,
|
|
int port,
|
|
const char *ca_crt,
|
|
httpclient_data_t *client_data)
|
|
{
|
|
return _http_send(client, url, port, ca_crt, HTTPCLIENT_POST, client_data);
|
|
}
|
|
|
|
/**********************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
httpclient_t client;
|
|
httpclient_data_t http_client_data;
|
|
int receive_maxlen;
|
|
int already_received;
|
|
char *url;
|
|
int port;
|
|
iotx_http_method_t method;
|
|
char *header;
|
|
char *cert;
|
|
int timeout;
|
|
recvcallback recv_cb;
|
|
void *recv_ctx;
|
|
} wrapper_http_handle_t;
|
|
|
|
static int _replase_str(char **ptr, void *data)
|
|
{
|
|
if (*ptr != NULL)
|
|
{
|
|
HAL_Free(*ptr);
|
|
}
|
|
*ptr = HAL_Malloc(strlen(data) + 1);
|
|
if (*ptr == NULL)
|
|
{
|
|
return STATE_SYS_DEPEND_MALLOC;
|
|
}
|
|
memset(*ptr, 0, strlen(data) + 1);
|
|
memcpy(*ptr, data, strlen(data));
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
void *wrapper_http_init(void)
|
|
{
|
|
wrapper_http_handle_t *handle = HAL_Malloc(sizeof(wrapper_http_handle_t));
|
|
if (handle == NULL)
|
|
{
|
|
iotx_state_event(ITE_STATE_USER_INPUT, STATE_SYS_DEPEND_MALLOC, "malloc failed");
|
|
return NULL;
|
|
}
|
|
memset(handle, 0, sizeof(wrapper_http_handle_t));
|
|
|
|
return handle;
|
|
}
|
|
|
|
int wrapper_http_setopt(void *handle, iotx_http_option_t option, void *data)
|
|
{
|
|
wrapper_http_handle_t *http_handle = (wrapper_http_handle_t *)handle;
|
|
|
|
if (handle == NULL || data == NULL)
|
|
{
|
|
return STATE_USER_INPUT_INVALID;
|
|
}
|
|
|
|
switch (option)
|
|
{
|
|
case IOTX_HTTPOPT_URL:
|
|
{
|
|
_replase_str(&http_handle->url, data);
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_PORT:
|
|
{
|
|
if (*(int *)(data) < 0)
|
|
{
|
|
return STATE_USER_INPUT_HTTP_PORT;
|
|
}
|
|
http_handle->port = *(int *)(data);
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_METHOD:
|
|
{
|
|
http_handle->method = *(iotx_http_method_t *)data;
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_HEADER:
|
|
{
|
|
_replase_str(&http_handle->header, data);
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_CERT:
|
|
{
|
|
http_handle->cert = data;
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_TIMEOUT:
|
|
{
|
|
if (*(int *)(data) < 0)
|
|
{
|
|
return STATE_USER_INPUT_HTTP_TIMEOUT;
|
|
}
|
|
http_handle->timeout = *(int *)(data);
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_RECVCALLBACK:
|
|
{
|
|
http_handle->recv_cb = data;
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_RECVMAXLEN:
|
|
{
|
|
http_handle->receive_maxlen = *(int *)(data);
|
|
}
|
|
break;
|
|
case IOTX_HTTPOPT_RECVCONTEXT:
|
|
{
|
|
http_handle->recv_ctx = (recvcallback)data;
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
httpc_err("Unknown Option");
|
|
return STATE_USER_INPUT_HTTP_OPTION;
|
|
}
|
|
}
|
|
|
|
return STATE_SUCCESS;
|
|
}
|
|
|
|
int wrapper_http_perform(void *handle, void *data, int length)
|
|
{
|
|
int res = 0, received_this_time = 0;
|
|
iotx_time_t timer;
|
|
wrapper_http_handle_t *http_handle = (wrapper_http_handle_t *)handle;
|
|
char *response_payload = NULL;
|
|
|
|
if (handle == NULL)
|
|
{
|
|
return STATE_USER_INPUT_INVALID;
|
|
}
|
|
|
|
if ((http_handle->method == IOTX_HTTP_POST) &&
|
|
(data == NULL || length <= 0))
|
|
{
|
|
return STATE_USER_INPUT_HTTP_POST_DATA;
|
|
}
|
|
|
|
if (http_handle->receive_maxlen <= 0)
|
|
{
|
|
return STATE_USER_INPUT_INVALID;
|
|
}
|
|
|
|
response_payload = HAL_Malloc(http_handle->receive_maxlen);
|
|
if (response_payload == NULL)
|
|
{
|
|
return STATE_SYS_DEPEND_MALLOC;
|
|
}
|
|
memset(response_payload, 0, http_handle->receive_maxlen);
|
|
|
|
http_handle->client.header = http_handle->header;
|
|
http_handle->http_client_data.post_buf = data;
|
|
http_handle->http_client_data.post_buf_len = length;
|
|
http_handle->http_client_data.response_buf = response_payload;
|
|
http_handle->http_client_data.response_buf_len = http_handle->receive_maxlen;
|
|
|
|
res = _http_send(&http_handle->client, http_handle->url, http_handle->port,
|
|
http_handle->cert, (HTTPCLIENT_REQUEST_TYPE)http_handle->method, &http_handle->http_client_data);
|
|
if (res < STATE_SUCCESS)
|
|
{
|
|
HAL_Free(response_payload);
|
|
return res;
|
|
}
|
|
//HAL_SleepMs(500); //TODO 发送OTA请求后等一会
|
|
HAL_SleepMs(50); //测试加 Lyn20220419
|
|
iotx_time_init(&timer); //timer->time初始化为0
|
|
utils_time_countdown_ms(&timer, http_handle->timeout); //timer = c+(timeout=1000)
|
|
|
|
res = httpclient_recv_response(&http_handle->client, iotx_time_left(&timer), &http_handle->http_client_data);
|
|
if (res < STATE_SUCCESS)
|
|
{
|
|
httpc_err("httpclient_recv_response is error,res = %d", res);
|
|
HAL_Free(response_payload);
|
|
return res;
|
|
}
|
|
|
|
received_this_time = http_handle->http_client_data.response_content_len - http_handle->http_client_data.retrieve_len;
|
|
// received_this_time = http_handle->http_client_data.response_content_len - http_handle->http_client_data.retrieve_len -
|
|
// http_handle->already_received;
|
|
// logMsg_bs("====received_this_time(%d) = %d - %d - %d====\n", received_this_time,
|
|
// http_handle->http_client_data.response_content_len, http_handle->http_client_data.retrieve_len,http_handle->already_received);
|
|
|
|
// http_handle->already_received = http_handle->http_client_data.response_content_len -
|
|
// http_handle->http_client_data.retrieve_len;
|
|
|
|
if (res >= STATE_SUCCESS)
|
|
{
|
|
if (http_handle->recv_cb)
|
|
{
|
|
http_handle->recv_cb(response_payload, received_this_time, http_handle->http_client_data.response_content_len,
|
|
http_handle->recv_ctx);
|
|
}
|
|
}
|
|
|
|
HAL_Free(response_payload);
|
|
|
|
return received_this_time;
|
|
}
|
|
|
|
void wrapper_http_deinit(void **handle)
|
|
{
|
|
wrapper_http_handle_t **http_handle = (wrapper_http_handle_t **)handle;
|
|
|
|
if (handle == NULL || *handle == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((*http_handle)->url)
|
|
{
|
|
HAL_Free((*http_handle)->url);
|
|
}
|
|
|
|
if ((*http_handle)->header)
|
|
{
|
|
HAL_Free((*http_handle)->header);
|
|
}
|
|
|
|
httpclient_close(&(*http_handle)->client);
|
|
|
|
HAL_Free(*http_handle);
|
|
*handle = NULL;
|
|
}
|
|
|
|
/**********************************************************************************/
|
|
|
|
#endif
|