/** * 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 #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); }