luckfox-pico-sdk/sysdrv/source/mcu/rt-thread/components/http/http_api.c
luckfox-eng29 8f34c2760d project:build.sh: Added fastboot support; custom modifications to U-Boot and kernel implemented using patches.
project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between
the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door
for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more
usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp:
Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for
compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem.
sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device
tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately.
sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick
boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades.
sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for
fastboot.

Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2024-10-14 09:47:04 +08:00

575 lines
16 KiB
C

/*
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
*/
#include "http_api.h"
static size_t g_last_socket_read_size;
static size_t g_last_not_read_chunk_size;
static int g_chunk_data_over;
static char g_chunk_buffer[HTTP_PREPROCESSOR_FRAME_SIZE * 2];
static void get_ip_addr(char *host_name, char *ip_addr);
static void parse_url(const char *url, char *host, int *port, char *download_file_name);
static void get_ip_addr(char *host_name, char *ip_addr)
{
int i;
struct hostent *host = gethostbyname(host_name);
if (!host)
{
ip_addr = NULL;
return;
}
for (i = 0; host->h_addr_list[i]; i++)
{
strcpy(ip_addr, inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));
break;
}
}
static void parse_url(const char *url, char *host, int *port, char *download_file_name)
{
int j = 0, i;
int start = 0;
*port = HTTP_SOCKET_PORT;
char *patterns[] = {"http://", "https://", NULL};
for (i = 0; patterns[i]; i++)
{
if (strncmp(url, patterns[i], strlen(patterns[i])) == 0)
{
start = strlen(patterns[i]);
}
}
for (i = start; url[i] != '/' && url[i] != '\0'; i++, j++)
{
host[j] = url[i];
}
host[j] = '\0';
char *pos = strstr(host, ":");
if (pos)
{
sscanf(pos, ":%d", port);
}
for (i = 0; i < (int)strlen(host); i++)
{
if (host[i] == ':')
{
host[i] = '\0';
break;
}
}
j = 0;
for (i = start; url[i] != '\0'; i++)
{
if (url[i] == '/')
{
if (i != strlen(url) - 1)
{
j = 0;
}
continue;
}
else
{
download_file_name[j++] = url[i];
}
}
download_file_name[j] = '\0';
}
int is_chunked(struct http_res_header resp)
{
if (!strcmp(resp.transfer_encoding, "Chunked")
|| !strcmp(resp.transfer_encoding, "chunked"))
{
return 1;
}
return 0;
}
struct http_res_header parse_header(const char *response)
{
struct http_res_header resp;
char *pos = strstr(response, "HTTP/");
if (pos)
{
sscanf(pos, "%*s %d", &resp.status_code);
}
pos = strstr(response, "Content-Type:");
if (pos)
{
sscanf(pos, "%*s %s", resp.content_type);
}
pos = strstr(response, "Transfer-Encoding:");
if (pos)
{
sscanf(pos, "%*s %s", resp.transfer_encoding);
}
pos = strstr(response, "Content-Length:");
if (pos)
{
sscanf(pos, "%*s %ld", &resp.content_length);
}
pos = strstr(response, "Cookie:");
if (pos)
{
sscanf(pos, "%*s %s", resp.cookie);
}
pos = strstr(response, "Location:");
if (pos)
{
char *pEnd;
sscanf(pos, "%*s %s", resp.httpRedirectURL.pParam);
pEnd = strstr(resp.httpRedirectURL.pParam, "\r\n");
if (pEnd)
{
resp.httpRedirectURL.nLength = pEnd - resp.httpRedirectURL.pParam;
}
}
return resp;
}
/**
* @brief Init http according to url
* @param[in] url to http opened url
* @param[in] param1 response of read from socket and in post mode send data buf
* @param[out] param2 socket to return to operate and in post mode send data size
* @retval ota_status_t, OTA_STATUS_OK on success
*/
http_status_t http_open(char *url, char **param1, int *param2, int mode, char *cookie, int offset)
{
int i;
char host_addr[HTTP_HOST_ADDRESS_LENGTH] = {0};
int port = HTTP_SOCKET_PORT;
bool bSucc = RT_FALSE;
int socket_local;
struct hostent *host;
char *download_file_name = NULL;
char *header = NULL;
char *buf = NULL;
char *relative_url = NULL;
download_file_name = (char *)rt_malloc(HTTP_DOWNLOAD_FILE_NAME_LENGTH * 4);
if (download_file_name == NULL)
{
HTTP_ERR(" No memory!");
return HTTP_STATUS_ERR;
}
header = (char *)rt_malloc(HTTP_HEADER_LENGTH);
if (header == NULL)
{
HTTP_ERR(" No memory!");
rt_free(download_file_name);
return HTTP_STATUS_ERR;
}
struct sockaddr_in address;
parse_url(url, host_addr, &port, download_file_name);
int formatPos = strlen(download_file_name) - 3;
char *format = download_file_name + formatPos;
char *_url = strstr(url, "://") + 3;
if (0 == strncmp(format, "mp3", 3) || strstr(download_file_name, "update") != NULL)
{
/* is music url or update.img*/
relative_url = strstr(_url, "/");
HTTP_DBG(" url = %s. relative_url = %s ", url, relative_url);
}
else /* is not music url */
{
HTTP_DBG(" url = %s ", url);
relative_url = url;
}
socket_local = socket(AF_INET, SOCK_STREAM, 0);
address.sin_family = AF_INET;
if (socket_local == -1)
{
bSucc = RT_FALSE;
goto END;
}
if (!(host = gethostbyname(host_addr)))
{
HTTP_ERR("gethostbyname failed");
bSucc = RT_FALSE;
goto END;
}
memcpy(&address.sin_addr.s_addr, *(host->h_addr_list), sizeof(address.sin_addr.s_addr));
address.sin_port = htons(port);
if (connect(socket_local, (struct sockaddr *) &address, sizeof(struct sockaddr_in)) == -1)
{
HTTP_ERR("connect sock failed\n");
bSucc = RT_FALSE;
goto END;
}
HTTP_DBG("cfg->target:%s", url);
HTTP_DBG(" host_addr:%s", host_addr);
HTTP_DBG(" port:%d", port);
HTTP_DBG("downLD_file:%s\n", download_file_name);
if (mode == HTTP_GET)
{
sprintf(header, \
"GET %s HTTP/1.1\r\n"\
"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537(KHTML, like Gecko) Chrome/47.0.2526Safari/537.36\r\n"\
"Host: %s\r\n"\
"Range: bytes=%d-\r\n"\
"Connection: keep-alive\r\n"\
"\r\n"\
, relative_url, host_addr, offset);
}
else
{
sprintf(header, \
"POST %s HTTP/1.1\r\n"\
"Cookie:%s\r\n"
"User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537(KHTML, like Gecko) Chrome/47.0.2526Safari/537.36\r\n"\
"Host: %s\r\n"\
"Connection: keep-alive\r\n"\
"Content-Type: multipart/form-data; boundary=--------------------------957323426414134187562641\r\n"\
"Accept-Encoding: gzip, deflate\r\n"\
"Content-Length: %d\r\n"\
"Connection: keep-alive\r\n"\
"\r\n"\
, relative_url, cookie, host_addr, *param2);
}
if (write(socket_local, header, strlen(header)) == -1)
{
HTTP_DBG("write socket failed");
bSucc = RT_FALSE;
goto END;
}
if (mode == HTTP_POST)
{
if (write(socket_local, *param1, *param2) == -1)
{
HTTP_DBG("write post data failed");
bSucc = RT_FALSE;
goto END;
}
HTTP_WRN("http post upload data over");
}
int mem_size = HTTP_DOWNLOAD_FILE_NAME_LENGTH;
int length = 0;
int len;
char *response = *param1;
buf = (char *) rt_malloc(mem_size * sizeof(char));
int retry = 10;
while ((len = http_socket_read(socket_local, buf, 1)) != -1)
{
if (len == 0)
{
if (retry == 0)
break;
retry--;
}
else
{
retry = 10;
}
if (length + len >= mem_size)
{
mem_size *= 2;
char *temp = (char *)rt_realloc(response, sizeof(char) * mem_size);
if (temp == NULL)
{
HTTP_ERR("os_malloc failed %d\n", sizeof(char) * mem_size);
bSucc = RT_FALSE;
goto END;
}
response = temp;
*param1 = response;
}
buf[len] = '\0';
strcat(response, buf);
int flag = 0;
for (i = strlen(response) - 1; response[i] == '\n' || response[i] == '\r'; i--, flag++)
{
; //do nothing
}
if (flag == 4)
{
bSucc = RT_TRUE;
break;
}
length += len;
}
g_last_not_read_chunk_size = 0;
g_last_socket_read_size = 0;
g_chunk_data_over = 0;
END:
if (download_file_name)
rt_free(download_file_name);
if (header)
rt_free(header);
if (buf)
rt_free(buf);
if (!bSucc)
{
if (socket_local != -1)
{
close(socket_local);
}
return HTTP_STATUS_ERR;
}
*param2 = socket_local;
HTTP_DBG("http open success \n");
return HTTP_STATUS_OK;
}
int http_socket_read(int socket, char *data, size_t data_len)
{
int rs = -1;
int fd = socket;
int retry_time = 1;
int has_read = 0;
int need_read = data_len;
while (retry_time--)
{
fd_set fdr, fde;
int timeout = 500;
struct timeval time = {timeout / 1000, (timeout % 1000) * 1000};
//int ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&time, sizeof(struct timeval));
//if (ret != 0)
// HTTP_ERR("set sockops rcvtimeout fail");
FD_ZERO(&fdr);
FD_SET(fd, &fdr);
FD_ZERO(&fde);
FD_SET(fd, &fde);
rs = select(fd + 1, &fdr, NULL, &fde, &time);
if (rs == 0)
{
//HTTP_DBG("recv timeout!!!");
}
else if (rs < 0 || FD_ISSET(fd, &fde))
{
HTTP_ERR("recv failed: rs = %d", rs);
return -1; //socket err.
}
else if (FD_ISSET(fd, &fdr))
{
//HTTP_DBG("before recv ...\n");
//rs = recv(fd, data + has_read, need_read, MSG_WAITALL);
rs = recv(fd, data + has_read, need_read, 0);
//HTTP_DBG("recv return = %u\n", rs);
if (rs > 0)
{
has_read += rs;
need_read -= rs;
if (need_read <= 0)
{
break;
}
}
}
}
rs = has_read;
return rs;
}
int http_socket_read_chunk(int socket, char *data, size_t data_len)
{
size_t read_chunk_data_size = 0;
size_t bytes_left = g_last_socket_read_size;
int g_chunk_size = g_last_not_read_chunk_size;
HTTP_DBG("http_socket_read_chunk,bytes_left:%d,g_last_not_read_chunk_size:%d", \
g_last_socket_read_size, g_last_not_read_chunk_size);
g_last_socket_read_size = 0;
g_last_not_read_chunk_size = 0;
int have_read = bytes_left;
int read_size = 0;
int buffer_size = HTTP_PREPROCESSOR_FRAME_SIZE * 2;
if (g_chunk_data_over)
return 0;
while (read_chunk_data_size < data_len)
{
if (have_read < buffer_size)
{
read_size =
http_socket_read(socket, g_chunk_buffer + have_read, buffer_size - have_read);
if (read_size <= 0)
{
HTTP_WRN("cant read socket, maybe finish = %d", read_chunk_data_size);
#if 0
if (read_chunk_data_size)
{
g_last_socket_read_size = have_read;
g_last_not_read_chunk_size = g_chunk_size;
return read_chunk_data_size;
}
#endif
}
}
have_read += read_size;
HTTP_DBG("1 http_socket_read_chunk,bytes_left:%d", have_read);
retry:
if (g_chunk_size == 0)
{
int cr_lf_pos = 0;
int is_found_cr_lf = 0;
while (1)
{
for (; cr_lf_pos < have_read - 2; cr_lf_pos++)
{
if (g_chunk_buffer[cr_lf_pos] == '\r' && g_chunk_buffer[cr_lf_pos + 1] == '\n')
{
is_found_cr_lf = RT_TRUE;
break;
}
}
if (is_found_cr_lf)
{
break;
}
if (have_read < data_len)
{
read_size =
http_socket_read(socket, g_chunk_buffer + have_read, buffer_size - have_read);
if (read_size < 0)
{
HTTP_WRN("cant read socket1, maybe finish = %d", read_chunk_data_size);
//return read_chunk_data_size;
}
else
{
have_read += read_size;
}
}
}
if (is_found_cr_lf)
{
int n = sscanf(g_chunk_buffer, "%x", &g_chunk_size);
if (n != 1)
{
HTTP_ERR("chunk format error size = 0");
return read_chunk_data_size;
}
}
HTTP_WRN("cr_lf_pos:%d is_found_cr_lf:%d, g_chunk_size:%d",
cr_lf_pos,
is_found_cr_lf,
g_chunk_size);
have_read -= (cr_lf_pos + HTTP_CR_LF_LEN);
memmove(g_chunk_buffer, &g_chunk_buffer[cr_lf_pos + HTTP_CR_LF_LEN], have_read);
if (g_chunk_size == 0)
{
HTTP_WRN("read last chunk = %d", read_chunk_data_size);
g_chunk_data_over = 1;
return read_chunk_data_size;
}
}
else
{
if ((read_chunk_data_size + have_read) >= data_len)
{
if (have_read < g_chunk_size)
{
memcpy(&data[read_chunk_data_size],
g_chunk_buffer,
data_len - read_chunk_data_size);
g_last_socket_read_size = have_read - (data_len - read_chunk_data_size);
memmove(g_chunk_buffer, &g_chunk_buffer[data_len - read_chunk_data_size], g_last_socket_read_size);
g_last_not_read_chunk_size =
g_chunk_size - (data_len - read_chunk_data_size);
read_chunk_data_size = data_len;
HTTP_DBG("read chunk data = %d", read_chunk_data_size);
return read_chunk_data_size;
}
}
if (have_read >= (g_chunk_size + HTTP_CR_LF_LEN))
{
if (g_chunk_buffer[g_chunk_size] != '\r' || g_chunk_buffer[g_chunk_size + 1] != '\n')
{
HTTP_ERR("chunk format error lost \r\n");
return read_chunk_data_size;
}
if ((read_chunk_data_size + g_chunk_size) < data_len)
{
memcpy(&data[read_chunk_data_size], g_chunk_buffer, g_chunk_size);
memmove(g_chunk_buffer,
&g_chunk_buffer[g_chunk_size + HTTP_CR_LF_LEN],
have_read - (g_chunk_size + HTTP_CR_LF_LEN));
read_chunk_data_size += g_chunk_size;
//bytes_left = have_read - (g_chunk_size + HTTP_CR_LF_LEN);
have_read -= (g_chunk_size + HTTP_CR_LF_LEN);
g_chunk_size = 0;
goto retry;
}
else
{
memcpy(&data[read_chunk_data_size],
g_chunk_buffer,
data_len - read_chunk_data_size);
g_last_socket_read_size = have_read - (data_len - read_chunk_data_size);
memmove(g_chunk_buffer, &g_chunk_buffer[data_len - read_chunk_data_size], g_last_socket_read_size);
g_last_not_read_chunk_size =
g_chunk_size - (data_len - read_chunk_data_size);
read_chunk_data_size = data_len;
HTTP_DBG("read chunk data = %d", read_chunk_data_size);
return read_chunk_data_size;
}
}
}
}
return read_chunk_data_size;
}
http_status_t http_close(int socket)
{
close(socket);
return HTTP_STATUS_OK;
}