doubao_chat/lib/net/libduer-device/modules/HTTP/lightduer_http_client.c

2323 lines
77 KiB
C

/**
* Copyright (2017) Baidu Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* File: lightduer_http_client.c
* Author: Pan Haijun, Gang Chen(chengang12@baidu.com)
* Desc: HTTP Client
*/
#include "lightduer_http_client.h"
#include <string.h>
#include "lightduer_log.h"
#include "lightduer_ds_log_http.h"
#include "lightduer_timestamp.h"
#include "lightduer_lib.h"
#include "lightduer_memory.h"
#include "lightduer_ds_log.h"
#include "lightduer_net_transport_wrapper.h"
#include "lightduer_sleep.h"
const char *duer_engine_get_rsa_cacrt(void);
#define HC_CHECK_ERR(p_client, ret, log_str, http_res) \
do{ \
if(ret) { \
DUER_LOGE(log_str "socket error (%d)", ret); \
return http_res; \
} \
} while(0)
static const int DUER_HTTP_CR_LF_LEN = 2; //DUER_STRLEN("\r\n")
static const int DUER_HTTP_SCHEME_LEN_MAX = 8;
static const int DUER_HTTP_HOST_LEN_MAX = 255;
static const int DUER_HTTP_REDIRECTION_MAX = 30;
static const int DUER_HTTP_TIME_OUT = (2 * 1000); //TIME OUT IN MILI-SECOND
static const int DUER_HTTP_CONNECT_TIME_OUT = (5 * 1000); //TIME OUT IN MILI-SECOND
static const int DUER_HTTP_TIME_OUT_RETRY_COUNT = 1;
static const int DUER_HTTP_REQUEST_CMD_LEN_MAX = 6;
static const int DUER_HTTP_CHUNK_SIZE = 1025;
static const int DUER_HTTP_RSP_CHUNK_SIZE = 100;
static const int DUER_HTTP_RESUME_COUNT_MAX = 10;
static const int DUER_HTTP_RESUME_CONNECT_COUNT_MAX = 3;
static const int DUER_HTTP_RANGE_LEN_MAX = 10;
static const int DUER_HTTP_PORT_LEN_MAX = 5;
static const int DUER_HTTP_CONTENT_LEN_MAX = 10;
static const duer_u32_t DUER_HTTP_CLIENT_MAX_RESOLVE_IP_COUNT = 3;
static const int DUER_SCHEME_HTTP = 0;
static const int DUER_SCHEME_HTTPS = 1;
static const int DUER_DEFAULT_HTTP_PORT = 80;
static const int DUER_DEFAULT_HTTPS_PORT = 443;
const char *s_hc_request_cmd[] = {
"GET", //DUER_HTTP_GET
"POST", //DUER_HTTP_POST
"PUT", //DUER_HTTP_PUT
"DELETE", //DUER_HTTP_DELETE
"HEAD", //DUER_HTTP_HEAD
};
enum duer_http_response_code {
DUER_HTTP_MIN_VALID_RSP_CODE = 200,
DUER_HTTP_PARTIAL_CONTENT_RSP_CODE = 206,
DUER_HTTP_MIN_INVALID_RSP_CODE = 400,
};
duer_http_result_t duer_http_init_socket_ops(duer_http_client_t *p_client,
duer_http_socket_ops_t *p_ops,
void *socket_args)
{
DUER_LOGV("entry\n");
if (!p_client || !p_ops) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Args err!\n");
return DUER_HTTP_ERR_FAILED;
}
DUER_MEMMOVE(&p_client->ops, p_ops, sizeof(duer_http_socket_ops_t));
if (!p_client->ops.init) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("http sokcet ops.init is null!\n");
return DUER_HTTP_ERR_FAILED;
}
p_client->ops.n_handle = p_client->ops.init(socket_args);
if (p_client->ops.n_handle == 0) {
duer_ds_log_http(DUER_DS_LOG_HTTP_SOCKET_INIT_FAILED, NULL);
DUER_LOGE("http sokcet ops.init initialize failed!\n");
return DUER_HTTP_ERR_FAILED;
}
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
static void http_client_close_socket_connect(duer_http_client_t *p_client)
{
if (p_client->last_host) {
DUER_FREE(p_client->last_host);
p_client->last_host = NULL;
p_client->ops.close(p_client->ops.n_handle);
}
}
duer_http_result_t duer_http_init(duer_http_client_t *p_client)
{
DUER_LOGV("entry\n");
if (!p_client) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Args err: p_client is null!\n");
return DUER_HTTP_ERR_FAILED;
}
DUER_MEMSET(p_client, 0, sizeof(duer_http_client_t));
p_client->lock = duer_mutex_create();
if (!p_client->lock) {
DUER_LOGE("Failed to create http mutex");
return DUER_HTTP_ERR_FAILED;
}
p_client->max_resume_retry_count = DUER_HTTP_RESUME_COUNT_MAX;
p_client->max_timeout_retry_count = DUER_HTTP_TIME_OUT_RETRY_COUNT;
p_client->max_connect_retry_count = DUER_HTTP_RESUME_CONNECT_COUNT_MAX;
p_client->timeout_ms = DUER_HTTP_TIME_OUT;
p_client->connect_timeout_ms = DUER_HTTP_CONNECT_TIME_OUT;
duer_ds_log_http(DUER_DS_LOG_HTTP_INIT, NULL);
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
void duer_http_destroy(duer_http_client_t *p_client)
{
DUER_LOGD("entry,%p\n", p_client);
if (!p_client) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Args err: p_client is null!\n");
return;
}
if (p_client->connect_keep_timer) {
duer_timer_stop(p_client->connect_keep_timer);
duer_timer_release(p_client->connect_keep_timer);
p_client->connect_keep_timer = NULL;
}
if (p_client->lock) {
duer_mutex_lock(p_client->lock);
}
http_client_close_socket_connect(p_client);
if (p_client->lock) {
duer_mutex_unlock(p_client->lock);
}
DUER_LOGD("duer_http_destroy");
if (p_client->ops.n_handle) {
p_client->ops.destroy(p_client->ops.n_handle);
p_client->ops.n_handle = 0;
}
if (p_client->lock) {
duer_mutex_destroy(p_client->lock);
}
DUER_MEMSET(p_client, 0, sizeof(duer_http_client_t));
duer_ds_log_http(DUER_DS_LOG_HTTP_DESTROYED, NULL);
DUER_LOGV("leave\n");
return;
}
void duer_http_set_cust_headers(duer_http_client_t *p_client, const char **headers, size_t pairs)
{
DUER_LOGV("entry\n");
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->pps_custom_headers = headers;
p_client->sz_headers_count = pairs;
DUER_LOGV("leave\n");
}
void duer_http_reg_data_hdlr(duer_http_client_t *p_client,
duer_http_data_handler data_hdlr_cb,
void *p_usr_ctx)
{
DUER_LOGV("entry\n");
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->data_hdlr_cb = data_hdlr_cb;
p_client->p_data_hdlr_ctx = p_usr_ctx;
DUER_LOGV("leave\n");
}
void duer_http_reg_url_get_cb(duer_http_client_t *p_client, duer_http_get_url_cb_t cb)
{
DUER_LOGV("entry\n");
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->get_url_cb = cb;
DUER_LOGV("leave\n");
}
void duer_http_reg_keep_retry_cb(duer_http_client_t *p_client, duer_http_keep_retry_cb cb)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->keep_retry_cb = cb;
}
void duer_http_reg_rsp_header_cb(duer_http_client_t *p_client, duer_http_header_cb cb)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->rsp_header_cb = cb;
}
void duer_http_reg_inform_ip_cb(duer_http_client_t *p_client, duer_http_inform_ip_cb cb)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->inform_ip_cb = cb;
}
void duer_http_set_max_resume_retry_count(duer_http_client_t *p_client, size_t count)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->max_resume_retry_count = count;
}
void duer_http_set_max_timeout_retry_count(duer_http_client_t *p_client, size_t count)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->max_timeout_retry_count = count;
}
void duer_http_set_connect_timeout_ms(duer_http_client_t *p_client, size_t timeout_ms)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->connect_timeout_ms = timeout_ms;
}
void duer_http_set_recv_timeout_ms(duer_http_client_t *p_client, size_t timeout_ms)
{
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return;
}
p_client->timeout_ms = timeout_ms;
}
void duer_http_stop_notify(duer_http_client_t *p_client)
{
DUER_LOGV("entry\n");
if (p_client) {
p_client->stop_flag = 1;
}
DUER_LOGV("leave\n");
}
int duer_http_get_rsp_code(duer_http_client_t *p_client)
{
DUER_LOGV("entry\n");
if (!p_client) {
DUER_LOGE("Args err: p_client is null!\n");
return -1;
}
DUER_LOGV("leave\n");
return p_client->n_http_response_code;
}
/**
* DESC:
* parse http url
*
* PARAM:
* @param[in] url: url to be parsed
* @param[out] scheme: protocal scheme buffer
* @param[in] scheme_len: length of arg "scheme"
* @param[out] host: http host name buffer
* @param[in] host_len: length of arg "host"
* @param[out] port: http port number
* @param[out] path: http path buffer
* @param[in] path_len: length of arg "path"
* @param[in] is_relative_url: the url is a relative url or not
* a relative url do not have host/port,
* the previous url's host/port should be used
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_parse_url(const char *url,
char *scheme,
size_t scheme_len,
char *host,
unsigned short *port,
char *path,
size_t path_len,
duer_bool is_relative_url)
{
size_t r_host_len = 0;
size_t r_path_len = 0;
char *p_path = NULL;
char *p_fragment = NULL;
char *p_host = NULL;
char *p_scheme = (char *) url;
DUER_LOGV("entry\n");
if (is_relative_url) {
p_path = (char *)url;
} else {
p_host = (char *)DUER_STRSTR(url, "://");
if (p_host == NULL) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Could not find host");
return DUER_HTTP_ERR_PARSE; //URL is invalid
}
//to find http scheme
if ((int)scheme_len < p_host - p_scheme + 1) { //including NULL-terminating char
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Scheme str is too small (%d >= %d)", scheme_len, p_host - p_scheme + 1);
return DUER_HTTP_ERR_PARSE;
}
DUER_MEMMOVE(scheme, p_scheme, p_host - p_scheme);
scheme[p_host - p_scheme] = '\0';
//to find port
p_host += DUER_STRLEN("://");
char *p_port = DUER_STRCHR(p_host, ':');
//to find host
p_path = DUER_STRCHR(p_host, '/');
if (p_port != NULL) {
// the port is only follow the ":" before "/"
if ((p_path == NULL) || (p_port < p_path)) {
r_host_len = p_port - p_host;
p_port++;
if (DUER_SSCANF(p_port, "%hu", port) != 1) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Could not find port");
return DUER_HTTP_ERR_PARSE;
}
}
} else {
*port = 0;
}
if (r_host_len == 0) {
if (!p_path) {
r_host_len = DUER_STRLEN(p_host);
} else {
r_host_len = p_path - p_host;
}
}
if (r_host_len > DUER_HTTP_HOST_LEN_MAX) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Host is too long(%d > %d)!", r_host_len, DUER_HTTP_HOST_LEN_MAX);
return DUER_HTTP_ERR_PARSE;
}
DUER_MEMCPY(host, p_host, r_host_len);
host[r_host_len] = '\0';
}
if (p_path) {
//to find path
p_fragment = DUER_STRCHR(p_path, '#');
if (p_fragment != NULL) {
r_path_len = p_fragment - p_path;
} else {
r_path_len = DUER_STRLEN(p_path);
}
if (path_len < r_path_len + 1) { //including NULL-terminating char
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Path str is too small (%d >= %d)", path_len, r_path_len + 1);
return DUER_HTTP_ERR_PARSE;
}
DUER_MEMMOVE(path, p_path, r_path_len);
path[r_path_len] = '\0';
} else {
if (path_len < 2) { // "/" is needed if there is no path
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Path str is too small (%d >= 2)", path_len);
return DUER_HTTP_ERR_PARSE;
}
path[0] = '/';
path[1] = '\0';
}
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
/**
* DESC:
* wrapper socket "send" to send data
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] buf: data to be sent
* @param[in] len: data length to be sent, if it's 0, then it equals the 'buf' 's length
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_send(duer_http_client_t *p_client,
const char *buf,
size_t len)
{
size_t written_len = 0;
int ret = 0;
size_t elapsed_ms = 0;
const size_t SLEEP_MS = 10;
DUER_LOGV("entry\n");
if (len == 0) {
len = DUER_STRLEN(buf);
}
DUER_LOGD("send %d bytes:(%s)\n", len, buf);
p_client->ops.set_blocking(p_client->ops.n_handle, true);
while (written_len < len && !p_client->stop_flag && elapsed_ms <= p_client->timeout_ms) {
ret = p_client->ops.send(p_client->ops.n_handle, buf + written_len, len - written_len);
if (ret > 0) {
elapsed_ms = 0;
written_len += ret;
p_client->upload_size += ret;
} else if (ret == 0) {
duer_ds_log_http(DUER_DS_LOG_HTTP_CONNECT_CLOSED_BY_SERVER, NULL);
DUER_LOGE("Connection was closed by server");
return DUER_HTTP_CLOSED;
} else if (ret == DUER_ERR_TRANS_WOULD_BLOCK) {
duer_sleep(SLEEP_MS);
elapsed_ms += SLEEP_MS;
} else {
duer_ds_log_http_report_err_code(DUER_DS_LOG_HTTP_SEND_FAILED, ret);
DUER_LOGE("Connection error (send returned %d)", ret);
return DUER_HTTP_ERR_CONNECT;
}
}
DUER_LOGD("Written %d bytes", written_len);
DUER_LOGV("entry\n");
return DUER_HTTP_OK;
}
/**
* DESC:
* wrapper socket "recv" to receive data
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] buf: a buffer to store received data
* @param[in] max_len: max size needed to read
* @param[out] p_read_len: really size the data is read
* @param[in] to_try: 0 means return when any data received,
* 1 means try to recv more data until buf full or timeout too many times
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_recv(duer_http_client_t *p_client,
char *buf,
size_t max_len,
size_t *p_read_len,
int to_try)
{
int ret = 0;
size_t sz_read_len = 0;
duer_http_result_t res = DUER_HTTP_OK;
duer_u32_t start_time = 0;
duer_u32_t now = 0;
duer_u32_t elapsed = 0;
DUER_LOGV("entry\n");
DUER_LOGV("Trying to read %d bytes", max_len);
start_time = duer_timestamp();
elapsed = start_time;
while (sz_read_len < max_len) {
DUER_LOGD("Trying(%d ) times to read at most %4d bytes [Not blocking] %d,%d",
p_client->timeout_count, max_len - sz_read_len, max_len, sz_read_len);
p_client->ops.set_blocking(p_client->ops.n_handle, true);
p_client->ops.set_timeout(p_client->ops.n_handle, p_client->timeout_ms);
ret = p_client->ops.recv(p_client->ops.n_handle, buf + sz_read_len, max_len - sz_read_len);
if (ret > 0) {
sz_read_len += ret;
if (!to_try) {
break;
}
if (sz_read_len < max_len) {
now = duer_timestamp();
if (DUER_TIME_AFTER(now, start_time + p_client->timeout_ms)) {
DUER_LOGI("Timeout count: %u, max_count: %u, read length: %u",
p_client->timeout_count, p_client->max_timeout_retry_count, sz_read_len);
++p_client->timeout_count;
start_time = now;
}
}
} else if (ret == 0) {
duer_ds_log_http(DUER_DS_LOG_HTTP_CONNECT_CLOSED_BY_SERVER, NULL);
res = DUER_HTTP_CLOSED;
break;
} else {
DUER_LOGI("Timeout count: %u, max_count: %u, read length: %u, ret: %d",
p_client->timeout_count, p_client->max_timeout_retry_count, sz_read_len, ret);
if ((ret != DUER_ERR_TRANS_TIMEOUT) && (ret != DUER_ERR_TRANS_WOULD_BLOCK)) {
DUER_LOGI("http recv error:%d", ret);
duer_ds_log_http_report_err_code(DUER_DS_LOG_HTTP_RECEIVE_FAILED, ret);
res = DUER_HTTP_ERR_CONNECT;
break;
}
++p_client->timeout_count;
start_time = duer_timestamp();
}
if (p_client->timeout_count > p_client->max_timeout_retry_count) {
res = DUER_HTTP_ERR_TIMEOUT;
break;
}
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
break;
}
}
elapsed = duer_timestamp() - elapsed;
if (elapsed > p_client->timeout_ms) {
DUER_LOGE("recv %d bytes, elapsed %u ms more than %u timeout",
sz_read_len, elapsed, p_client->timeout_ms);
}
DUER_LOGD("Read %d bytes, expecte %d, res=%d.", sz_read_len, max_len, res);
*p_read_len = sz_read_len;
DUER_LOGV("leave\n");
return res;
}
/**
* DESC:
* send http request header
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] method: method to comunicate with server
* @param[in] path: resource path
* @param[in] host: server host
* @param[in] port: server port
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_send_request(duer_http_client_t *p_client,
duer_http_method_t method,
char *path,
char *host,
unsigned short port,
size_t len,
const char *type)
{
int ret = 0;
int req_fmt_len = 0;
char *p_request_buf = NULL;
duer_bool is_default_port = DUER_FALSE;
DUER_LOGV("entry");
req_fmt_len = DUER_STRLEN("%s %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nRange: bytes=%d-" \
"Content-Length:%u\r\nContent-Type:%s\r\n");
req_fmt_len += DUER_HTTP_REQUEST_CMD_LEN_MAX + DUER_STRLEN(path) + DUER_STRLEN(host) \
+ DUER_HTTP_PORT_LEN_MAX + DUER_HTTP_RANGE_LEN_MAX + DUER_HTTP_CONTENT_LEN_MAX,
+ (type == NULL ? 0 : DUER_STRLEN(type));
p_request_buf = (char *)DUER_MALLOC(req_fmt_len);
if (!p_request_buf) {
DUER_DS_LOG_REPORT_HTTP_MEMORY_ERROR();
DUER_LOGE("Mallco memory(size:%d) for request buffer Failed!\n", req_fmt_len);
return DUER_HTTP_ERR_FAILED;
}
DUER_MEMSET(p_request_buf, 0, req_fmt_len);
if (p_client->scheme == DUER_SCHEME_HTTPS) {
if (port == DUER_DEFAULT_HTTPS_PORT) {
is_default_port = DUER_TRUE;
}
} else {
if (port == DUER_DEFAULT_HTTP_PORT) {
is_default_port = DUER_TRUE;
}
}
if (is_default_port) {
DUER_SNPRINTF(p_request_buf, req_fmt_len, "%s %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\n",
s_hc_request_cmd[method], path, host);
} else {
DUER_SNPRINTF(p_request_buf, req_fmt_len, "%s %s HTTP/1.1\r\nHost: %s:%d\r\nAccept: */*\r\n",
s_hc_request_cmd[method], path, host, port);
}
if (method == DUER_HTTP_POST || method == DUER_HTTP_PUT) {
DUER_SNPRINTF(p_request_buf + DUER_STRLEN(p_request_buf),
req_fmt_len - DUER_STRLEN(p_request_buf),
"Content-Length:%u\r\nContent-Type:%s\r\n",
len,
type);
}
if (p_client->recv_size > 0) {
DUER_SNPRINTF(p_request_buf + DUER_STRLEN(p_request_buf),
req_fmt_len - DUER_STRLEN(p_request_buf),
"Range: bytes=%d-\r\n",
p_client->recv_size);
}
DUER_LOGD(" request buffer %d bytes{%s}", req_fmt_len, p_request_buf);
ret = duer_http_send(p_client, p_request_buf, 0);
DUER_FREE(p_request_buf);
if (ret != DUER_HTTP_OK) {
duer_ds_log_http_report_err_code(DUER_DS_LOG_HTTP_SEND_FAILED, ret);
DUER_LOGE("Could not write request");
return DUER_HTTP_ERR_CONNECT;
}
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
/**
* DESC:
* sending all http headers
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] buff: provide buffer to send headers
* @param[in] buff_size: 'buff' 's size
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_send_headers(duer_http_client_t *p_client,
char *buff,
int buff_size)
{
size_t nh = 0;
int header_len = 0;
duer_http_result_t ret = DUER_HTTP_OK;
DUER_LOGV("enrty\n");
DUER_LOGD("Send custom header(s) %d (if any)", p_client->sz_headers_count);
for (nh = 0; nh < p_client->sz_headers_count * 2; nh += 2) {
if (!p_client->pps_custom_headers[nh] || !p_client->pps_custom_headers[nh + 1]) {
DUER_LOGE("Invalid custom header");
return DUER_HTTP_ERR_FAILED;
}
DUER_LOGD("hdr[%2d] %s: %s", nh, p_client->pps_custom_headers[nh],
p_client->pps_custom_headers[nh + 1]);
header_len = DUER_STRLEN("%s: %s\r\n") + DUER_STRLEN(p_client->pps_custom_headers[nh]) \
+ DUER_STRLEN(p_client->pps_custom_headers[nh + 1]);
if (header_len >= buff_size) {
DUER_LOGE("Http header (%d) is too long!", header_len);
return DUER_HTTP_ERR_FAILED;
}
DUER_SNPRINTF(buff, buff_size, "%s: %s\r\n", p_client->pps_custom_headers[nh],
p_client->pps_custom_headers[nh + 1]);
ret = duer_http_send(p_client, buff, 0);
HC_CHECK_ERR(p_client, ret, "Could not write headers", DUER_HTTP_ERR_CONNECT);
DUER_LOGD("send() returned %d", ret);
}
//Close headers
DUER_LOGD("Close Http Headers");
ret = duer_http_send(p_client, "\r\n", 0);
HC_CHECK_ERR(p_client, ret, "Close http headers failed!", ret);
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
/**
* DESC:
* set http content length
*
* PARAM:
* @param[in] content_len: http content lenght to be set
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static void duer_http_set_content_len(duer_http_client_t *p_client, int content_len)
{
DUER_LOGV("enrty\n");
p_client->n_http_content_len = content_len;
DUER_LOGV("leave\n");
}
/**
* DESC:
* set http content type
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] p_content_type: http content type to be set
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static void duer_http_set_content_type(duer_http_client_t *p_client, char *p_content_type)
{
DUER_LOGV("enrty\n");
if (p_content_type) {
DUER_SNPRINTF(p_client->p_http_content_type,
DUER_HTTP_CONTENT_TYPE_LEN_MAX,
"%s",
p_content_type);
if (DUER_STRLEN(p_content_type) >= DUER_HTTP_CONTENT_TYPE_LEN_MAX) {
p_client->p_http_content_type[DUER_HTTP_CONTENT_TYPE_LEN_MAX - 1] = '\0';
}
}
DUER_LOGV("leave\n");
}
/**
* DESC:
* receive http response and parse it
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in/out] buf: [in]pass a buffer to store received data;
* [out]to store the remaining received data except http response
* @param[in] buf_size: size of buffer
* @param[out] p_read_len: the remaining size of the data in 'buf'
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_recv_rsp_and_parse(duer_http_client_t *p_client,
char *buf,
size_t buf_size,
size_t *p_read_len)
{
int ret = 0;
size_t cr_lf_pos = 0;
size_t rev_len = 0;
size_t max_len = buf_size - 1;
char *p_cr_lf = NULL;
DUER_LOGV("enrty\n");
ret = duer_http_recv(p_client, buf, max_len, &rev_len, 1);
buf[rev_len] = '\0';
DUER_LOGD("response received: %s\n", buf);
if (ret != DUER_HTTP_OK) {
duer_ds_log_http_report_err_code(DUER_DS_LOG_HTTP_RESPONSE_RECEIVE_FAILED, ret);
HC_CHECK_ERR(p_client, ret, "Receiving http response error!", DUER_HTTP_ERR_CONNECT);
}
p_cr_lf = DUER_STRSTR(buf, "\r\n");
if (!p_cr_lf) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Response Protocol error",
DUER_HTTP_ERR_PRTCL);
}
cr_lf_pos = p_cr_lf - buf;
buf[cr_lf_pos] = '\0';
//to parse HTTP response
if (DUER_SSCANF(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &p_client->n_http_response_code) != 1) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
//cannot match string, error
DUER_LOGE("Not a correct HTTP answer : {%s}\n", buf);
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Response Protocol error",
DUER_HTTP_ERR_PRTCL);
}
if (p_client->n_http_response_code < DUER_HTTP_MIN_VALID_RSP_CODE) {
//Did not return a 2xx code;
//TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
DUER_LOGE("Response code %d", p_client->n_http_response_code);
duer_ds_log_http_report_err_code(DUER_DS_LOG_HTTP_RESPONSE_RECEIVE_FAILED,
p_client->n_http_response_code);
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Response Protocol error",
DUER_HTTP_ERR_PRTCL);
}
if ((p_client->recv_size > 0)
&& (p_client->n_http_response_code == DUER_HTTP_MIN_VALID_RSP_CODE)) {
DUER_LOGE("Server doesn't support Range");
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Response Protocol error",
DUER_HTTP_ERR_PRTCL);
}
rev_len -= (cr_lf_pos + DUER_HTTP_CR_LF_LEN);
DUER_MEMMOVE(buf, &buf[cr_lf_pos + DUER_HTTP_CR_LF_LEN], rev_len);
buf[rev_len] = '\0';
if (p_read_len) {
*p_read_len = rev_len;
}
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
/**
* DESC:
* receive http response and parse it
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in/out] buf: [in]pass a buffer to store received data;
* [out]to store the remaining received data except http response
* @param[in] buf_size: size of the buffer
* @param[in/out] p_read_len: [in/out]the remaining size of the data in 'buf'
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_recv_headers_and_parse(duer_http_client_t *p_client,
char *buf,
size_t buf_size,
size_t *p_read_len)
{
int i = 0;
char *key = NULL;
char *value = NULL;
char *p_cr_lf = NULL;
char *new_location = NULL;
size_t len = 0;
size_t max_len = buf_size - 1;
size_t value_len = 0;
size_t cr_lf_pos = 0;
size_t rev_len = 0;
size_t new_rev_len = 0;
size_t location_len = 0;
size_t remain_pos = 0;
duer_http_result_t res = DUER_HTTP_OK;
int http_content_len = 0;
duer_bool is_location_found = DUER_FALSE;
duer_bool is_value_overlength = DUER_FALSE;
DUER_LOGV("enrty\n");
HC_CHECK_ERR(p_client, !p_read_len, "arg 'p_read_len' is null!", DUER_HTTP_ERR_FAILED);
rev_len = *p_read_len;
p_client->n_is_chunked = false;
while (true) {
p_cr_lf = DUER_STRSTR(buf, "\r\n");
if (p_cr_lf == NULL) {
if (rev_len < max_len) {
res = duer_http_recv(p_client, buf + rev_len, max_len - rev_len,
&new_rev_len, 0);
rev_len += new_rev_len;
buf[rev_len] = '\0';
DUER_LOGD("total data:%d, new receive %d chars: [%s]", rev_len, new_rev_len, buf);
if (res != DUER_HTTP_OK) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
HC_CHECK_ERR(p_client, res, "Receiving http headers error!", res);
}
continue;
}
}
key = NULL;
value = NULL;
if (p_cr_lf) {
cr_lf_pos = p_cr_lf - buf;
buf[cr_lf_pos] = '\0';
}
if (is_value_overlength) {
value = buf;
} else {
//end of headers to break
if (cr_lf_pos == 0) {
DUER_LOGD("All Http Headers readed over!");
rev_len -= DUER_HTTP_CR_LF_LEN;
//Be sure to move NULL-terminating char as well
DUER_MEMMOVE(buf, &buf[DUER_HTTP_CR_LF_LEN], rev_len + 1);
break;
}
// parse the header
for (i = 0; i < cr_lf_pos; i++) {
if (buf[i] == ':') {
buf[i] = '\0';
key = buf;
value = &buf[i + 1];
break;
}
}
if ((key == NULL) || (value == NULL)) {
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Could not parse header",
DUER_HTTP_ERR_PRTCL);
}
if (!p_cr_lf) {
is_value_overlength = DUER_TRUE;
}
while ((*key != '\0') && ((*key == ' ') || (*key == ' '))) {
key++;
}
while ((*value != '\0') && ((*value == ' ') || (*value == ' '))) {
value++;
}
DUER_LOGD("Read header (%s: %s)", key, value);
if (p_client->rsp_header_cb != NULL) {
p_client->rsp_header_cb(p_client->p_data_hdlr_ctx, key, value);
}
if (!DUER_STRNCASECMP(key, "Content-Length", max_len)) {
DUER_SSCANF(value, "%d", &http_content_len);
duer_http_set_content_len(p_client, http_content_len + p_client->recv_size);
DUER_LOGI("Total size of the data need to read: %d bytes",
http_content_len + p_client->recv_size);
} else if (!DUER_STRNCASECMP(key, "Transfer-Encoding", max_len)) {
if (!DUER_STRNCASECMP(value, "Chunked", max_len - (value - buf))
|| !DUER_STRNCASECMP(value, "chunked", max_len - (value - buf))) {
p_client->n_is_chunked = true;
DUER_LOGI("Http client data is chunked transfored\n");
if (p_client->p_chunk_buf == NULL) {
p_client->p_chunk_buf = (char *)DUER_MALLOC(DUER_HTTP_CHUNK_SIZE);
if (!p_client->p_chunk_buf) {
DUER_DS_LOG_REPORT_HTTP_MEMORY_ERROR();
HC_CHECK_ERR(p_client,
!p_client->p_chunk_buf,
"Malloc memory failed for p_chunk_buf!",
DUER_HTTP_ERR_FAILED);
}
}
}
} else if (!DUER_STRNCASECMP(key, "Content-Type", max_len)) {
duer_http_set_content_type(p_client, value);
} else if (!DUER_STRNCASECMP(key, "Location", max_len)) {
if (p_client->p_location) {
DUER_FREE(p_client->p_location);
p_client->p_location = NULL;
}
is_location_found = DUER_TRUE;
} else {
// do nothing
}
}
if (p_cr_lf) {
remain_pos = cr_lf_pos + DUER_HTTP_CR_LF_LEN;
} else {
// the latest character might be '\r', hence it should be processed next time
remain_pos = rev_len - 1;
}
if (is_location_found) {
if (p_cr_lf) {
value_len = DUER_STRLEN(value);
} else {
value_len = DUER_STRLEN(value) - 1;
}
location_len = p_client->p_location ? DUER_STRLEN(p_client->p_location) : 0;
len = value_len + location_len + 1;
if (p_client->p_location) {
new_location = DUER_REALLOC(p_client->p_location, len);
if (!new_location) {
DUER_FREE(p_client->p_location);
p_client->p_location = NULL;
DUER_DS_LOG_REPORT_HTTP_MEMORY_ERROR();
HC_CHECK_ERR(p_client, DUER_HTTP_ERR_FAILED,
"Malloc memory failed for p_location!", DUER_HTTP_ERR_FAILED);
}
p_client->p_location = new_location;
DUER_STRNCAT(p_client->p_location, value, value_len);
} else {
p_client->p_location = DUER_MALLOC(len);
if (!p_client->p_location) {
DUER_DS_LOG_REPORT_HTTP_MEMORY_ERROR();
HC_CHECK_ERR(p_client, DUER_HTTP_ERR_FAILED,
"Malloc memory failed for p_location!", DUER_HTTP_ERR_FAILED);
}
DUER_SNPRINTF(p_client->p_location, len, "%s", value);
}
}
// Including the NULL-Terminal
DUER_MEMMOVE(buf, &buf[remain_pos], rev_len - remain_pos + 1);
rev_len -= remain_pos;
DUER_LOGD("remain_pos: %d, rev_len:%d", remain_pos, rev_len);
if (p_cr_lf) {
is_value_overlength = DUER_FALSE;
}
if (is_location_found && !is_value_overlength) {
*p_read_len = rev_len;
return DUER_HTTP_REDIRECTTION;
}
}
*p_read_len = rev_len;
DUER_LOGV("leave!remain data: %s\n", buf);
return DUER_HTTP_OK;
}
/**
* DESC:
* receive http chunked size, and save it into duer_http_client_t's 'sz_chunk_size'
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in/out] buf: [in]pass a buffer to store received data;
* [out]to store the remaining received data except http response
* @param[in] max_len: max size needed to read each time
* @param[in/out] p_read_len: [in&out]the remaining size of the data in 'buf'
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_recv_chunked_size(duer_http_client_t *p_client,
char *buf,
size_t max_len,
size_t *p_read_len)
{
int n = 0;
int found_cr_lf = false;
size_t rev_len = 0;
size_t new_rev_len = 0;
size_t cr_lf_pos = 0;
duer_http_result_t res = DUER_HTTP_OK;
DUER_LOGV("enrty\n");
HC_CHECK_ERR(p_client, !p_read_len, "arg 'p_read_len' is null!", DUER_HTTP_ERR_FAILED);
DUER_LOGD("args:max_len=%d, *p_read_len=%d", max_len, *p_read_len);
rev_len = *p_read_len;
while (!found_cr_lf) {
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
return res;
}
// to check chunked size
if (rev_len >= DUER_HTTP_CR_LF_LEN) {
for (cr_lf_pos = 0; cr_lf_pos < rev_len - 2; cr_lf_pos++) {
if (buf[cr_lf_pos] == '\r' && buf[cr_lf_pos + 1] == '\n') {
found_cr_lf = true;
DUER_LOGD("Found cr&lf at index %d", cr_lf_pos);
break;
}
}
}
if (found_cr_lf) {
break;
}
//Try to read more
if (rev_len < max_len) {
res = duer_http_recv(p_client, buf + rev_len, max_len - rev_len, &new_rev_len, 0);
rev_len += new_rev_len;
buf[rev_len] = '\0';
DUER_LOGD("Receive %d chars in buf: \n[%s]", new_rev_len, buf);
if (res != DUER_HTTP_OK) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
HC_CHECK_ERR(p_client, res, "Receiving http chunk headers error!", res);
}
} else {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Get http chunked size failed:not found cr lf in size line!",
DUER_HTTP_ERR_PRTCL);
}
}
buf[cr_lf_pos] = '\0';
DUER_LOGD("found_cr_lf=%d, cr_lf_pos=%d, buf=%s", found_cr_lf, cr_lf_pos, buf);
//read chunked size
n = DUER_SSCANF(buf, "%x", (unsigned int *)&p_client->sz_chunk_size);
if (n != 1) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
HC_CHECK_ERR(p_client,
DUER_HTTP_ERR_PRTCL,
"Get http chunked size failed: Could not read chunk length!",
DUER_HTTP_ERR_PRTCL);
}
DUER_LOGD("HTTP chunked size:%d\n", p_client->sz_chunk_size);
rev_len -= (cr_lf_pos + DUER_HTTP_CR_LF_LEN);
DUER_MEMMOVE(buf, &buf[cr_lf_pos + DUER_HTTP_CR_LF_LEN],
rev_len + 1); //Be sure to move NULL-terminating char as well
*p_read_len = rev_len;
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
/**
* DESC:
* receiving http chunk data
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] buf: to store received data with 'data_len_in_buf' data in it at first
* @param[in] max_len: max size needed to read each time
* @param[in] data_len_in_buf: data remained in 'buf' at first
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_recv_chunk(duer_http_client_t *p_client,
char *buf,
size_t max_len,
size_t data_len_in_buf)
{
size_t need_to_read = 0;
size_t rev_len = 0;
size_t temp = 0;
size_t new_rev_len = 0;
size_t chunk_buff_len = 0;
size_t max_read_len = 0;
duer_http_result_t res = DUER_HTTP_OK;
DUER_LOGV("enrty\n");
DUER_LOGD("args:max_len=%d, data_len_in_buf=%d", max_len, data_len_in_buf);
if (data_len_in_buf > max_len) {
DUER_LOGE("Args err, data over buffer(%d > %d):", data_len_in_buf, max_len);
HC_CHECK_ERR(p_client, DUER_HTTP_ERR_FAILED, "Args err!", DUER_HTTP_ERR_FAILED);
}
rev_len = data_len_in_buf;
while (true) {
res = duer_http_recv_chunked_size(p_client, buf, max_len, &rev_len);
if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_CHUNK_SIZE_RECEIVE_FAILED, NULL);
DUER_LOGE("Receiving http chunked size error:res=%d", res);
goto RET;
}
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
goto RET;
}
DUER_LOGD("*************HTTP chunk size is %d************\n", p_client->sz_chunk_size);
//last chunk to return
if (p_client->sz_chunk_size == 0) {
DUER_LOGD("Receive the last HTTP chunk!!!");
//to flush the last chunk buffer in the "RET"
if (chunk_buff_len) {
//add NULL terminate for print string
p_client->p_chunk_buf[chunk_buff_len] = '\0';
}
goto RET;
}
//when chunk size is less than max_len of "buf"
if (p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN <= max_len) {
if (rev_len < p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN) {
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
goto RET;
}
res = duer_http_recv(
p_client,
buf + rev_len,
p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN - rev_len,
&new_rev_len,
1);
rev_len += new_rev_len;
buf[rev_len] = '\0';
DUER_LOGD("Receive %d chars in buf: \n[%s]", new_rev_len, buf);
if (res != DUER_HTTP_OK) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Receiving http chunked data error:res=%d", res);
goto RET;
}
}
if (rev_len < p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN) {
res = DUER_HTTP_ERR_PRTCL;
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("No CR&LF after http data chunk: DUER_HTTP_ERR_PRTCL");
goto RET;
}
//when received data is more than a chunk with "\r\n"
if (rev_len >= p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN) {
//for -1 reserve the last bytes to do nothing in p_chunk_buf
if (chunk_buff_len + p_client->sz_chunk_size >= DUER_HTTP_CHUNK_SIZE - 1) {
temp = DUER_HTTP_CHUNK_SIZE - 1 - chunk_buff_len;
DUER_MEMMOVE(p_client->p_chunk_buf + chunk_buff_len, buf, temp);
//add NULL terminate for print string
p_client->p_chunk_buf[DUER_HTTP_CHUNK_SIZE - 1] = '\0';
chunk_buff_len += temp;
//call callback to output
if (p_client->data_hdlr_cb) {
p_client->data_hdlr_cb(p_client->p_data_hdlr_ctx, p_client->data_pos,
p_client->p_chunk_buf, chunk_buff_len,
p_client->p_http_content_type);
p_client->recv_size += chunk_buff_len;
}
p_client->data_pos = DUER_HTTP_DATA_MID;
DUER_MEMMOVE(p_client->p_chunk_buf,
buf + temp,
p_client->sz_chunk_size - temp);
chunk_buff_len = p_client->sz_chunk_size - temp;
} else {
DUER_MEMMOVE(p_client->p_chunk_buf + chunk_buff_len, buf,
p_client->sz_chunk_size);
chunk_buff_len += p_client->sz_chunk_size;
}
//remove "\r\n"
if (buf[p_client->sz_chunk_size] != '\r'
|| buf[p_client->sz_chunk_size + 1] != '\n') {
res = DUER_HTTP_ERR_PRTCL;
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("No CR&LF after http data chunk when remove cr&lf");
goto RET;
}
rev_len -= (p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN);
//Be sure to move NULL-terminating char as well
DUER_MEMMOVE(buf, &buf[p_client->sz_chunk_size + DUER_HTTP_CR_LF_LEN], rev_len + 1);
}
} else { //when chunk size with "\r\n" is more than max_len of "buf"
DUER_LOGD("Here to read large HTTP chunk (%d) bytes!", p_client->sz_chunk_size);
if (p_client->sz_chunk_size <= rev_len) {
res = DUER_HTTP_ERR_FAILED;
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Receiving http large chunked data error: sz_chunk_size <= rev_len!");
goto RET;
}
//read a large chunk data, (1)read all chunk data first
need_to_read = p_client->sz_chunk_size - rev_len;
while (need_to_read) {
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
goto RET;
}
if (max_len - rev_len > need_to_read) {
max_read_len = need_to_read;
} else {
max_read_len = max_len - rev_len;
}
res = duer_http_recv(p_client, buf + rev_len, max_read_len, &new_rev_len, 1);
rev_len += new_rev_len;
need_to_read -= new_rev_len;
buf[rev_len] = '\0';
DUER_LOGD("Receive %d chars in buf: \n", new_rev_len);
if (res != DUER_HTTP_OK) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Receiving http large chunk data error:res=%d", res);
goto RET;
}
//for sub 1 reserve '\0' the last bytes to do nothing in p_chunk_buf
if (chunk_buff_len + rev_len >= DUER_HTTP_CHUNK_SIZE - 1) {
temp = DUER_HTTP_CHUNK_SIZE - 1 - chunk_buff_len;
DUER_MEMMOVE(p_client->p_chunk_buf + chunk_buff_len, buf, temp);
//add NULL terminate for print string
p_client->p_chunk_buf[DUER_HTTP_CHUNK_SIZE - 1] = '\0';
chunk_buff_len += temp;
//call callback to output
if (p_client->data_hdlr_cb) {
//wait_ms(1000); use for latency test
p_client->data_hdlr_cb(p_client->p_data_hdlr_ctx, p_client->data_pos,
p_client->p_chunk_buf, chunk_buff_len,
p_client->p_http_content_type);
p_client->recv_size += chunk_buff_len;
}
p_client->data_pos = DUER_HTTP_DATA_MID;
DUER_MEMMOVE(p_client->p_chunk_buf, buf + temp, rev_len - temp);
chunk_buff_len = rev_len - temp;
} else {
DUER_MEMMOVE(p_client->p_chunk_buf + chunk_buff_len, buf, rev_len);
chunk_buff_len += rev_len;
}
rev_len = 0;
}
DUER_LOGD("Read a large HTTP chunk (%d) bytes finished", p_client->sz_chunk_size);
//(2)to receive "\r\n" and remove it
DUER_LOGD("receive cr&lf and remove it");
res = duer_http_recv(p_client, buf, DUER_HTTP_CR_LF_LEN, &new_rev_len, 1);
buf[new_rev_len] = '\0';
DUER_LOGD("Receive %d chars in buf: \n[0x%x, 0x%x]", new_rev_len, buf[0], buf[1]);
if (res != DUER_HTTP_OK) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Receiving cr&lf http large chunked data error:res=%d", res);
goto RET;
}
if ((new_rev_len != DUER_HTTP_CR_LF_LEN) || buf[0] != '\r' || buf[1] != '\n') {
res = DUER_HTTP_ERR_PRTCL;
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("No CR&LF after http large data chunk:DUER_HTTP_ERR_PRTCL");
goto RET;
}
// to reset rev_len when run here
rev_len = 0;
}
}//end of while(true)
RET:
if ((res == DUER_HTTP_OK) && (chunk_buff_len > 0)) {
if (p_client->data_hdlr_cb) {
p_client->data_hdlr_cb(p_client->p_data_hdlr_ctx,
p_client->data_pos,
p_client->p_chunk_buf,
chunk_buff_len,
p_client->p_http_content_type);
p_client->recv_size += chunk_buff_len;
p_client->data_pos = DUER_HTTP_DATA_MID;
}
}
DUER_LOGV("leave\n");
return res;
}
/**
* DESC:
* receive http data
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] buf: to store received data with 'data_len_in_buf' data in it at first
* @param[in] max_len: max size needed to read each time
* @param[in] data_len_in_buf: data remained in 'buf' at first
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_recv_content_data(duer_http_client_t *p_client,
char *buf,
size_t max_len,
size_t data_len_in_buf)
{
size_t need_to_read = p_client->n_http_content_len - p_client->recv_size;
size_t rev_len = data_len_in_buf;
size_t new_rev_len = 0;
duer_http_result_t res = DUER_HTTP_OK;
DUER_LOGD("enrty\n");
if (data_len_in_buf > max_len || data_len_in_buf > need_to_read) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Args err, data over buffer(%d > %d or %d):",
data_len_in_buf, max_len, need_to_read);
HC_CHECK_ERR(p_client, DUER_HTTP_ERR_FAILED, "Args err!", DUER_HTTP_ERR_FAILED);
}
need_to_read -= rev_len;
while (need_to_read || rev_len) {
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
break;
}
DUER_LOGD("lefting %d bytes to read for all %d bytes(%f)", need_to_read,
p_client->n_http_content_len, 1.0 * need_to_read / p_client->n_http_content_len);
res = duer_http_recv(
p_client, buf + rev_len,
(max_len - rev_len) > need_to_read ? need_to_read : (max_len - rev_len),
&new_rev_len,
1);
rev_len += new_rev_len;
need_to_read -= new_rev_len;
buf[rev_len] = '\0';
DUER_LOGV("Receive %d bytes total %d bytes in buf: \n[%s]", new_rev_len, rev_len, buf);
if (rev_len) {
if (!p_client->data_hdlr_cb) {
DUER_LOGE("Data handler callback is NULL!");
res = DUER_HTTP_ERR_FAILED;
goto RET;
}
p_client->data_hdlr_cb(p_client->p_data_hdlr_ctx, p_client->data_pos, buf,
rev_len, p_client->p_http_content_type);
p_client->recv_size += rev_len;
p_client->data_pos = DUER_HTTP_DATA_MID;
}
if (res != DUER_HTTP_OK) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Receive http data finished !errcode= %d when remaining %d bytes to receive",
res, need_to_read);
break;
}
if (need_to_read > 0 && rev_len == 0) {
DUER_DS_LOG_REPORT_HTTP_COMMON_ERROR();
DUER_LOGE("Remaining %d bytes cannot receive,and give up\n", need_to_read);
res = DUER_HTTP_ERR_FAILED;
break;
}
rev_len = 0;
}
RET:
DUER_LOGD("leave\n");
return res;
}
/**
* DESC:
* get http download progress
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[out] total_size: to receive the total size to be download
* If chunked Transfer-Encoding is used, we cannot know the total size
* untile download finished, in this case the total size will be 0
* @param[out] recv_size: to receive the data size have been download
*
* @RETURN none
*/
void duer_http_get_download_progress(duer_http_client_t *p_client,
size_t *total_size,
size_t *recv_size)
{
if (!p_client || !total_size || !recv_size) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Args error!\n");
return;
}
*total_size = p_client->n_http_content_len;
*recv_size = p_client->recv_size;
}
/**
* DESC:
* parse the redirect location, create the new path if it is a relative url
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] p_path: the path of the old url
* @param[in] path_len: the max length of path
* @param[out] is_relative_url: return whether it is a relative url
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_parse_redirect_location(duer_http_client_t *p_client,
const char *p_path,
const size_t path_len,
bool *is_relative_url)
{
char *new_path = NULL;
size_t len = 0;
DUER_LOGV("enrty\n");
if ((!p_client) || (!is_relative_url) || (!p_path)) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Invalid param!\n");
return DUER_HTTP_ERR_PARSE;
}
if (!p_client->p_location) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("Invalid location!\n");
return DUER_HTTP_ERR_PARSE;
}
if (p_client->p_location[0] == 'h') {
*is_relative_url = false;
return DUER_HTTP_OK;
}
*is_relative_url = true;
// the relative path's format may be "./path", "../path"
if (p_client->p_location[0] != '/') {
// one more byte for '/', one more byte for '\0'
len = DUER_STRLEN(p_path) + DUER_STRLEN(p_client->p_location) + 2;
new_path = DUER_MALLOC(len);
if (!new_path) {
DUER_DS_LOG_REPORT_HTTP_MEMORY_ERROR();
DUER_LOGE("No enough memory!\n");
return DUER_HTTP_ERR_PARSE;
}
DUER_SNPRINTF(new_path, len, "%s", p_path);
// for example: the previous path is "p1/p2", the relative path is "../",
// the new path should be "p1/p2/../", we should add a '/'
if (p_path[DUER_STRLEN(p_path)] != '/') {
DUER_STRNCAT(new_path, "/", 1);
}
DUER_STRNCAT(new_path, p_client->p_location, DUER_STRLEN(p_client->p_location));
DUER_LOGI("new path: %s\n", new_path);
DUER_FREE(p_client->p_location);
p_client->p_location = NULL;
p_client->p_location = new_path;
}
DUER_LOGV("leave\n");
return DUER_HTTP_OK;
}
static int duer_http_socket_do_connect(duer_http_client_t *p_client,
const char *host,
unsigned short port)
{
if (p_client->stop_flag) {
return DUER_CANCEL;
}
#ifdef DUER_HTTP_DNS_SUPPORT
duer_ip_list_t *ip_list = NULL;
duer_auto_dns_resolve_type_t resolve_type;
int ret = duer_auto_dns_resolve_multi_ip_with_type(host, DUER_HTTP_CLIENT_MAX_RESOLVE_IP_COUNT,
&ip_list, &resolve_type);
if (ip_list == NULL) {
if (ret == DUER_OK) {
ret = DUER_ERR_TRANS_DNS_FAIL;
}
if (p_client->inform_ip_cb && !p_client->stop_flag) {
p_client->inform_ip_cb(p_client->p_data_hdlr_ctx,
host, NULL, DUER_AUTO_DNS_RESOLVE_FAILED, ret);
}
return ret;
}
DUER_LOGI("Host: %s, resolved ip count: %u, connect_timeout: %u", host, ip_list->count,
p_client->connect_timeout_ms);
for (duer_u32_t i = 0; i < ip_list->count; ++i) {
if (p_client->stop_flag) {
ret = DUER_CANCEL;
break;
}
ret = p_client->ops.connect(p_client->ops.n_handle, host, ip_list->list[i], port,
p_client->connect_timeout_ms);
if (p_client->inform_ip_cb && !p_client->stop_flag) {
char ip[DUER_HTTP_DNS_IP_LEN_MAX];
inet_ntoa_r(ip_list->list[i], ip, DUER_HTTP_DNS_IP_LEN_MAX);
p_client->inform_ip_cb(p_client->p_data_hdlr_ctx, host, ip, resolve_type, ret);
}
if (ret == DUER_OK) {
break;
}
}
duer_http_free_ip_list(ip_list);
#else
int ret = p_client->ops.connect(p_client->ops.n_handle, host, 0, port,
p_client->connect_timeout_ms);
#endif // DUER_HTTP_DNS_SUPPORT
return ret;
}
static duer_http_result_t duer_http_socket_connect(duer_http_client_t *p_client,
const char *host,
const unsigned short port)
{
bool is_need_connect = false;
duer_http_result_t ret = DUER_HTTP_OK;
int rs = 0;
int len = 0;
duer_mutex_lock(p_client->lock);
if (!p_client->last_host) {
is_need_connect = true;
} else if (DUER_STRNCMP(host, p_client->last_host, DUER_HTTP_HOST_LEN_MAX)
|| (port != p_client->last_port)) {
is_need_connect = true;
http_client_close_socket_connect(p_client);
} else {
p_client->ops.set_timeout(p_client->ops.n_handle, 0);
// p_client->ops.set_timeout(p_client->ops.n_handle, p_client->timeout_ms);
rs = p_client->ops.recv(p_client->ops.n_handle, p_client->buf, 1);
// connect had been closed by server or error happened
if ((rs == 0) || ((rs < 0) && (rs != DUER_ERR_TRANS_WOULD_BLOCK))) {
is_need_connect = true;
http_client_close_socket_connect(p_client);
} else {
ret = DUER_HTTP_OK;
}
}
duer_mutex_unlock(p_client->lock);
if (!is_need_connect) {
DUER_LOGI("The connection still alive");
goto RET;
}
DUER_LOGI("Http reconnect");
p_client->timeout_count = 0;
rs = p_client->ops.open(p_client->ops.n_handle);
if (rs >= 0) {
if (p_client->scheme == DUER_SCHEME_HTTPS) {
const char *ca_pem = duer_engine_get_rsa_cacrt();
if (!ca_pem) {
DUER_LOGE("Failed to get ca pem");
ret = DUER_HTTP_ERR_FAILED;
goto RET;
}
duer_trans_set_pk((duer_trans_handler)p_client->ops.n_handle, ca_pem, DUER_STRLEN(ca_pem) + 1);
} else {
duer_trans_unset_pk((duer_trans_handler)p_client->ops.n_handle);
}
p_client->ops.set_timeout(p_client->ops.n_handle, p_client->timeout_ms);
rs = duer_http_socket_do_connect(p_client, host, port);
DUER_LOGD("n_handle %d, rs: %d", p_client->ops.n_handle, rs);
if (rs < 0) {
if (rs == DUER_INF_TRANS_IP_BY_HTTP_DNS) {
DUER_LOGI("ip got by http dns");
duer_ds_log_http(DUER_DS_LOG_HTTP_DNS_GET_IP, NULL);
} else if (rs == DUER_ERR_TRANS_WOULD_BLOCK) {
DUER_LOGD("http connect would block");
} else if (rs == DUER_CANCEL) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
ret = rs;
goto RET;
} else {
p_client->ops.close(p_client->ops.n_handle);
DUER_LOGE("Failed to connect socket, rs: %d", rs);
ret = (rs == DUER_ERR_TRANS_DNS_FAIL ? DUER_HTTP_ERR_DNS : DUER_HTTP_ERR_CONNECT);
goto RET;
}
}
} else {
ret = DUER_HTTP_ERR_CONNECT;
DUER_LOGE("Failed to open socket, rs: %d", rs);
goto RET;
}
len = DUER_STRLEN(host);
duer_mutex_lock(p_client->lock);
p_client->last_host = (char *)DUER_MALLOC(len + 1);
if (!p_client->last_host) {
DUER_LOGE("Memory overlow");
ret = DUER_HTTP_ERR_FAILED;
} else {
DUER_SNPRINTF(p_client->last_host, len + 1, "%s", host);
p_client->last_host[len] = '\0';
p_client->last_port = port;
}
duer_mutex_unlock(p_client->lock);
RET:
return ret;
}
/**
* DESC:
* connect to the given url
*
* PARAM:
* @param[in] p_client: pointer of the http client
* @param[in] url: url to be connected
* @param[in] method: method to comunicate with server
* @param[out] remain_size_in_buf: how many data received when connect
*
* RETURN DUER_HTTP_OK if success, other duer_http_result_t type code if failed
*/
static duer_http_result_t duer_http_connect_and_send_req(duer_http_client_t *p_client,
const char *url,
duer_http_method_t method,
size_t *remain_size_in_buf,
const char *data,
size_t len,
const char *type)
{
char scheme[DUER_HTTP_SCHEME_LEN_MAX];
char *p_path = NULL;
char *host = NULL;
bool is_relative_url = false;
int path_len_max = 0;
int redirect_count = 0;
size_t rev_len = 0;
unsigned short port = 0;
duer_http_result_t res = DUER_HTTP_OK;
DUER_LOGV("enrty\n");
host = (char *)DUER_MALLOC(DUER_HTTP_HOST_LEN_MAX + 1); //including NULL-terminating char
if (host == NULL) {
DUER_LOGE("No memory!");
return DUER_HTTP_ERR_PARSE;
}
while (redirect_count <= DUER_HTTP_REDIRECTION_MAX) {
if (!url) {
duer_ds_log_http_report_with_url(DUER_DS_LOG_HTTP_URL_PARSE_FAILED, NULL);
DUER_LOGE("url is NULL!");
res = DUER_HTTP_ERR_FAILED;
break;
}
if (DUER_HTTP_CHUNK_SIZE <= DUER_HTTP_RSP_CHUNK_SIZE) {
DUER_LOGE("http client chunk size config error!!!\n");
res = DUER_HTTP_ERR_FAILED;
break;
}
path_len_max = DUER_STRLEN(url) + 1;
p_path = (char *)DUER_MALLOC(path_len_max);
if (!p_path) {
DUER_DS_LOG_REPORT_HTTP_MEMORY_ERROR();
DUER_LOGE("Mallco memory(size:%d) Failed!\n", path_len_max);
res = DUER_HTTP_ERR_FAILED;
break;
}
port = 0;
//to parse url
res = duer_http_parse_url(url, scheme, sizeof(scheme), host, &port, p_path,
path_len_max, is_relative_url);
if (res != DUER_HTTP_OK) {
duer_ds_log_http_report_with_url(DUER_DS_LOG_HTTP_URL_PARSE_FAILED, url);
DUER_LOGE("parseURL returned %d", res);
res = DUER_HTTP_ERR_FAILED;
break;
}
p_client->scheme = DUER_SCHEME_HTTP;
if (!strncmp(scheme, "HTTPS", strlen("HTTPS"))
|| !strncmp(scheme, "https", strlen("https"))) {
p_client->scheme = DUER_SCHEME_HTTPS;
}
//to set default port
if (port == 0) { //to do handle HTTPS->443
if (p_client->scheme == DUER_SCHEME_HTTPS) {
port = DUER_DEFAULT_HTTPS_PORT;
} else {
port = DUER_DEFAULT_HTTP_PORT;
}
}
DUER_LOGD("Scheme: %s", scheme);
DUER_LOGD("Host: %s", host);
DUER_LOGD("Port: %d", port);
DUER_LOGD("Path: %s", p_path);
//to connect server
DUER_LOGD("Connecting socket to server");
res = duer_http_socket_connect(p_client, host, port);
if (res == DUER_CANCEL) {
break;
}
if (res != DUER_HTTP_OK) {
duer_ds_log_http_report_with_url(DUER_DS_LOG_HTTP_SOCKET_CONN_FAILED, url);
DUER_LOGE("Could not connect(%d): %s:%d\n", res, host, port);
res = DUER_HTTP_ERR_CONNECT;
break;
}
DUER_LOGD("Connecting socket to server success:%d", res);
//to send http request
DUER_LOGD("Sending request");
res = duer_http_send_request(p_client, method, p_path, host, port, len, type);
if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_REQUEST_SEND_FAILED, NULL);
break;
}
//to send http headers
res = duer_http_send_headers(p_client, p_client->buf, DUER_HTTP_CHUNK_SIZE);
if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_HEADER_SEND_FAILED, NULL);
break;
}
//sending http post data
if ((method == DUER_HTTP_POST || method == DUER_HTTP_PUT) && (data != NULL) && (len != 0)) {
res = duer_http_send(p_client, data, len);
if (res != DUER_HTTP_OK) {
break;
}
}
//to receive http response
DUER_LOGD("Receiving http response");
res = duer_http_recv_rsp_and_parse(p_client,
p_client->buf,
DUER_HTTP_RSP_CHUNK_SIZE + 1,
&rev_len);
if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_RESPONSE_RECEIVE_FAILED, NULL);
break;
}
//to receive http headers and pasrse it
DUER_LOGD("Receiving http headers");
res = duer_http_recv_headers_and_parse(p_client,
p_client->buf,
DUER_HTTP_CHUNK_SIZE,
&rev_len);
if (p_client->n_http_response_code >= DUER_HTTP_MIN_INVALID_RSP_CODE) {
DUER_LOGE("Response code %d", p_client->n_http_response_code);
duer_ds_log_http_report_err_code(DUER_DS_LOG_HTTP_RESPONSE_RECEIVE_FAILED,
p_client->n_http_response_code);
res = DUER_HTTP_ERR_PRTCL;
break;
}
if (res == DUER_HTTP_REDIRECTTION) {
DUER_LOGI("DUER_HTTP_REDIRECTTION");
redirect_count++;
duer_mutex_lock(p_client->lock);
http_client_close_socket_connect(p_client);
duer_mutex_unlock(p_client->lock);
res = duer_http_parse_redirect_location(p_client, p_path,
path_len_max, &is_relative_url);
if (res != DUER_HTTP_OK) {
break;
}
DUER_FREE(p_path);
p_path = NULL;
url = p_client->p_location;
DUER_LOGI("Taking %d redirections to [%s]", redirect_count, url);
continue;
} else if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_HEADER_RECEIVE_FAILED, NULL);
break;
} else {
if (p_client->get_url_cb) {
p_client->get_url_cb(p_client->p_data_hdlr_ctx, url);
}
break;
}
}
if (res != DUER_CANCEL && res != DUER_HTTP_OK && redirect_count > 0) {
duer_ds_log_http_redirect_fail(p_client->p_location);
}
if (redirect_count > DUER_HTTP_REDIRECTION_MAX) {
DUER_LOGE("Too many redirections!");
if (res == DUER_HTTP_OK) {
res = DUER_HTTP_TOO_MANY_REDIRECT;
}
duer_ds_log_http_too_many_redirect(redirect_count);
}
if (p_path) {
DUER_FREE(p_path);
}
if (host) {
DUER_FREE(host);
}
*remain_size_in_buf = rev_len;
DUER_LOGV("leave\n");
return res;
}
duer_http_result_t duer_http_get_data(duer_http_client_t *p_client, size_t remain_size_in_buf)
{
duer_http_result_t res = DUER_HTTP_OK;
DUER_LOGV("enrty\n");
//to receive http chunk data
if (p_client->n_is_chunked) {
DUER_LOGD("Receiving http chunk data\n");
res = duer_http_recv_chunk(p_client, p_client->buf, DUER_HTTP_CHUNK_SIZE - 1,
remain_size_in_buf);
if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_CHUNK_RECEIVE_FAILED, NULL);
}
} else {
//to receive http data content
DUER_LOGD("Receiving http data content\n");
res = duer_http_recv_content_data(p_client, p_client->buf, DUER_HTTP_CHUNK_SIZE - 1,
remain_size_in_buf);
if (res != DUER_HTTP_OK) {
duer_ds_log_http(DUER_DS_LOG_HTTP_CONTENT_RECEIVE_FAILED, NULL);
}
}
if (res == DUER_HTTP_OK) {
DUER_LOGI("Completed HTTP transaction");
}
DUER_LOGV("leave\n");
return res;
}
static void duer_http_conn_keep_timer_cb(void *argument)
{
duer_ds_log_http_persisten_conn_timeout(((duer_http_client_t *)argument)->last_host);
duer_mutex_lock(((duer_http_client_t *)argument)->lock);
http_client_close_socket_connect(argument);
duer_mutex_unlock(((duer_http_client_t *)argument)->lock);
}
static void duer_http_ds_log_statistic(duer_http_client_t *p_client,
int start_time,
const size_t pos,
int ret,
const char *url)
{
int time_spend = 0;
duer_ds_log_http_statistic_t statistic_info;
duer_ds_log_http_code_t code = DUER_DS_LOG_HTTP_DOWNLOAD_FINISHED;
DUER_MEMSET(&statistic_info, 0, sizeof(duer_ds_log_http_statistic_t));
statistic_info.url = url;
statistic_info.ret_code = ret;
statistic_info.upload_size = p_client->upload_size;
statistic_info.content_len = p_client->n_http_content_len;
statistic_info.is_chunked = p_client->n_is_chunked;
statistic_info.download_size = p_client->recv_size - pos;
statistic_info.resume_count = p_client->resume_retry_count;
time_spend = duer_timestamp() - start_time;
if (time_spend > 0) {
statistic_info.download_speed = ((float)statistic_info.download_size) / time_spend * 1000;
}
if (ret != DUER_HTTP_OK) {
code = DUER_DS_LOG_HTTP_DOWNLOAD_FAILED;
} else if (p_client->stop_flag) {
code = DUER_DS_LOG_HTTP_DOWNLOAD_STOPPED;
} else {
code = DUER_DS_LOG_HTTP_DOWNLOAD_FINISHED;
}
duer_ds_log_http_download_exit(code, &statistic_info);
}
static duer_http_result_t duer_http_func(duer_http_client_t *p_client,
const char *url,
duer_http_method_t method,
const size_t pos,
const int connect_keep_time,
const char *data,
size_t len,
const char *type)
{
size_t rev_len = 0;
duer_http_result_t ret = DUER_HTTP_OK;
int started_time = 0;
size_t connect_retry_count = 0;
size_t last_recv_size = 0;
duer_ds_log_http_code_t log_code = 0;
DUER_LOGV("enrty\n");
if (!p_client || !url) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("PARAM error");
return DUER_HTTP_ERR_FAILED;
}
switch (method) {
case DUER_HTTP_GET:
log_code = DUER_DS_LOG_HTTP_DOWNLOAD_STARTED;
break;
case DUER_HTTP_POST:
log_code = DUER_DS_LOG_HTTP_POST_STARTED;
break;
case DUER_HTTP_PUT:
case DUER_HTTP_DELETE:
break;
default:
DUER_LOGE("Invalid param");
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
return DUER_ERR_INVALID_PARAMETER;
}
duer_ds_log_http_report_with_url(log_code, url);
DUER_LOGD("http %s url: %s\n", s_hc_request_cmd[method], url);
started_time = duer_timestamp();
duer_mutex_lock(p_client->lock);
if (p_client->connect_keep_timer) {
duer_timer_stop(p_client->connect_keep_timer);
duer_timer_release(p_client->connect_keep_timer);
p_client->connect_keep_timer = NULL;
}
duer_mutex_unlock(p_client->lock);
p_client->data_pos = DUER_HTTP_DATA_FIRST;
p_client->resume_retry_count = 0;
p_client->timeout_count = 0;
p_client->recv_size = pos;
p_client->upload_size = 0;
p_client->stop_flag = 0;
duer_http_set_content_len(p_client, 0);
p_client->buf = DUER_MALLOC(DUER_HTTP_CHUNK_SIZE);
if (!p_client->buf) {
ret = DUER_HTTP_ERR_FAILED;
goto RET;
}
while (p_client->resume_retry_count <= p_client->max_resume_retry_count
&& connect_retry_count <= p_client->max_connect_retry_count) {
if (p_client->stop_flag) {
DUER_LOGI("Stopped getting media data from url by stop flag!\n");
break;
}
// create http connect
// if (duer_wifi_is_connected()) {
ret = duer_http_connect_and_send_req(p_client, url, method, &rev_len, data, len, type);
// } else {
// ret = DUER_HTTP_ERR_CONNECT;
// }
if ((ret == DUER_HTTP_ERR_CONNECT) || (ret == DUER_HTTP_ERR_TIMEOUT)) {
if (p_client->keep_retry_cb != NULL
&& p_client->keep_retry_cb(p_client->p_data_hdlr_ctx) != 0) {
p_client->resume_retry_count = 0;
connect_retry_count = 0;
DUER_LOGI("Try to resume from break-point, keep retry");
/* Give up CPU */
duer_sleep(50);
} else {
++p_client->resume_retry_count;
++connect_retry_count;
DUER_LOGI("Try to resume from break-point %d time\n", p_client->resume_retry_count);
}
http_client_close_socket_connect(p_client);
continue;
} else if (ret == DUER_CANCEL) {
ret = DUER_HTTP_OK;
break;
}
if (ret != DUER_HTTP_OK) {
break;
}
// get http data
ret = duer_http_get_data(p_client, rev_len);
if ((ret == DUER_HTTP_ERR_CONNECT)
|| (ret == DUER_HTTP_ERR_TIMEOUT)
|| (ret == DUER_HTTP_CLOSED)) {
DUER_LOGW("duer http get data ret:%d", ret);
++p_client->resume_retry_count;
if (p_client->recv_size == last_recv_size) {
++connect_retry_count;
} else {
connect_retry_count = 0;
}
if (p_client->keep_retry_cb != NULL
&& p_client->keep_retry_cb(p_client->p_data_hdlr_ctx) != 0) {
DUER_LOGI("Resume from break-point: keep retry");
p_client->resume_retry_count = 0;
connect_retry_count = 0;
}
if (p_client->resume_retry_count <= p_client->max_resume_retry_count
&& connect_retry_count <= p_client->max_connect_retry_count) {
DUER_LOGI("Resume from break-point:retry %d time\n", p_client->resume_retry_count);
http_client_close_socket_connect(p_client);
} else {
DUER_LOGE("Resume from break-point too many times\n");
}
continue;
} else {
connect_retry_count = 0;
last_recv_size = p_client->recv_size;
}
break;
}
DUER_LOGI("HTTP: %d bytes received\n", p_client->recv_size - pos);
if (ret == DUER_HTTP_OK && p_client->recv_size == pos) {
duer_ds_log_http(DUER_DS_LOG_HTTP_ZERO_BYTE_DOWNLOAD, NULL);
}
RET:
if ((p_client->data_pos != DUER_HTTP_DATA_FIRST) && (p_client->data_hdlr_cb)) {
p_client->data_hdlr_cb(
p_client->p_data_hdlr_ctx,
DUER_HTTP_DATA_LAST,
NULL,
0,
p_client->p_http_content_type);
}
duer_http_ds_log_statistic(p_client, started_time, pos, ret, url);
duer_mutex_lock(p_client->lock);
if ((p_client->stop_flag) ||
(connect_keep_time == 0) || (ret != DUER_HTTP_OK)) {
http_client_close_socket_connect(p_client);
} else {
p_client->connect_keep_timer = duer_timer_acquire(duer_http_conn_keep_timer_cb,
p_client,
DUER_TIMER_ONCE);
if (p_client->connect_keep_timer) {
duer_timer_start(p_client->connect_keep_timer, connect_keep_time);
} else {
http_client_close_socket_connect(p_client);
DUER_LOGE("Failed to create timer to close persistent connect");
ret = DUER_HTTP_ERR_FAILED;
}
}
duer_mutex_unlock(p_client->lock);
if (p_client->p_location) {
DUER_FREE(p_client->p_location);
p_client->p_location = NULL;
}
if (p_client->p_chunk_buf) {
DUER_FREE(p_client->p_chunk_buf);
p_client->p_chunk_buf = NULL;
}
if (p_client->buf) {
DUER_FREE(p_client->buf);
p_client->buf = NULL;
}
DUER_LOGV("leave\n");
return ret;
}
duer_http_result_t duer_http_post(duer_http_client_t *p_client,
const char *url,
const int connect_keep_time,
const char *data,
size_t len,
const char *content_type)
{
return duer_http_perform(p_client, url, DUER_HTTP_POST, connect_keep_time, data, len,
content_type);
}
duer_http_result_t duer_http_get(duer_http_client_t *p_client,
const char *url,
const size_t pos,
const int connect_keep_time)
{
return duer_http_func(p_client, url, DUER_HTTP_GET, pos, connect_keep_time, NULL, 0, NULL);;
}
duer_http_result_t duer_http_perform(duer_http_client_t *p_client,
const char *url,
duer_http_method_t method,
const int connect_keep_time,
const char *data,
size_t len,
const char *content_type)
{
if ((len > 0) && ((data == NULL) || (content_type == NULL))) {
DUER_DS_LOG_REPORT_HTTP_PARAM_ERROR();
DUER_LOGE("PARAM error");
return DUER_HTTP_ERR_FAILED;
}
return duer_http_func(p_client, url, method, 0, connect_keep_time, data, len, content_type);
}