1725 lines
64 KiB
C
Raw Normal View History

2025-04-10 17:31:33 +08:00
/* Copyright (C) 2012 mbed.org, MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include "netdb.h"
#include "sockets.h"
#ifdef FEATURE_HTTP_TLS_ENABLE
#include "mbedtls/debug.h"
#include "ec_sslcmd_api.h"
#endif
#include "cmsis_os2.h"
#include "HTTPClient.h"
#include "sctdef.h"
#include DEBUG_LOG_HEADER_FILE
#define HTTP_CONNECT_TIMEOUT_EN
#define CHUNK_SIZE 1501
#define MAXHOST_SIZE 128
#define TEMPBUF_SIZE 512
#define MAX_TIMEOUT (10 * 60) //10 min
#ifndef MIN
#define MIN(x,y) (((x)<(y))?(x):(y))
#endif
#define CHECK_CONN_ERR(ret) \
do{ \
if(ret) { \
return HTTP_CONN; \
} \
} while(0)
#define CHECK_ERR(ret) \
do{ \
if(ret != HTTP_OK && ret != HTTP_CONN) { \
return ret; \
} \
} while(0)
#define PRTCL_ERR() \
do{ \
return HTTP_PRTCL; \
} while(0)
#define OVERFLOW_ERR(ret) \
do{ \
if(ret) { \
return HTTP_OVERFLOW; \
} \
} while(0)
#define DEBUG_LEVEL 2
AP_PLAT_COMMON_BSS static char *httpSendBuf = NULL;
AP_PLAT_COMMON_BSS static char *httpSendBufTemp = NULL;
//extern size_t xBytesTaskMalloced;
#ifdef FEATURE_HTTP_TLS_ENABLE
extern uint8_t ec_mbedtls_check_crt_valid;
extern sslSessionContext * gSslSessionContext;
#endif
/****************************************************************************************************************************
****************************************************************************************************************************
* static function
****************************************************************************************************************************
****************************************************************************************************************************/
#define __DEFINE_STATIC_FUNCTION__//just for easy to find this position
static HTTPResult parseURL(const char* url, char* scheme, int32_t maxSchemeLen, char* host, int32_t maxHostLen, uint16_t* port, char* path, int32_t maxPathLen) //Parse URL
{
ECPLAT_PRINTF(UNILOG_HTTP, parseURL_1, P_DEBUG, "url=%s", (uint8_t*)url);
char* schemePtr = (char*) url;
char* hostPtr = (char*) strstr(url, "://");
if (hostPtr == NULL) {
HTTPWARN("Could not find host");
return HTTP_PARSE; //URL is invalid
}
if ( (uint16_t)maxSchemeLen < hostPtr - schemePtr + 1 ) { //including NULL-terminating char
HTTPWARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
return HTTP_PARSE;
}
memcpy(scheme, schemePtr, hostPtr - schemePtr);
scheme[hostPtr - schemePtr] = '\0';
hostPtr += 3;
int32_t hostLen = 0;
char* portPtr = strchr(hostPtr, ':');
if( portPtr != NULL ) {
hostLen = portPtr - hostPtr;
portPtr++;
if( sscanf(portPtr, "%hu", port) != 1) {
HTTPWARN("Could not find port");
return HTTP_PARSE;
}
ECPLAT_PRINTF(UNILOG_HTTP, parseURL_2, P_DEBUG, "has port=%d, hostLen= %d", *port,hostLen);
} else {
hostLen = strlen(hostPtr);
ECPLAT_PRINTF(UNILOG_HTTP, parseURL_3, P_DEBUG, "no port, hostLen=%d", hostLen);
*port=0;
}
char* pathPtr = strchr(hostPtr, '/');
if( pathPtr != 0 && portPtr == 0) {
hostLen = pathPtr - hostPtr;
ECPLAT_PRINTF(UNILOG_HTTP, parseURL_4, P_DEBUG, "has path, hostLen=%d", hostLen);
}
if( maxHostLen < hostLen + 1 ) { //including NULL-terminating char
HTTPWARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
return HTTP_PARSE;
}
memcpy(host, hostPtr, hostLen);
host[hostLen] = '\0';
ECPLAT_PRINTF(UNILOG_HTTP, parseURL_5, P_DEBUG, "host=%s", (uint8_t*)host);
int32_t pathLen;
char* fragmentPtr = strchr(hostPtr, '#');
if(fragmentPtr != NULL) {
pathLen = fragmentPtr - pathPtr;
} else {
if(pathPtr != NULL){
pathLen = strlen(pathPtr);
} else {
pathLen = 0;
}
}
if( maxPathLen < pathLen + 1 ) { //including NULL-terminating char
HTTPWARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
return HTTP_PARSE;
}
if (pathPtr!= NULL && pathLen > 0) {
memcpy(path, pathPtr, pathLen);
path[pathLen] = '\0';
}
ECPLAT_PRINTF(UNILOG_HTTP, parseURL_6, P_DEBUG, "path=%s", (uint8_t*)path);
HTTPINFO("parseURL{url(%s),host(%s),maxHostLen(%d),port(%d),path(%s),maxPathLen(%d)}\r\n",
url, host, maxHostLen, *port, path, maxPathLen);
return HTTP_OK;
}
// Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
static int base64enc(const char *input, unsigned int length, char *output, int len)
{
static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned int c, c1, c2, c3;
if ((uint16_t)len < ((((length-1)/3)+1)<<2)) return -1;
for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) {
c1 = ((((unsigned char)*((unsigned char *)&input[i]))));
c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0;
c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0;
c = ((c1 & 0xFC) >> 2);
output[j+0] = base64[c];
c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4);
output[j+1] = base64[c];
c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6);
output[j+2] = (length>i+1)?base64[c]:'=';
c = (c3 & 0x3F);
output[j+3] = (length>i+2)?base64[c]:'=';
}
output[(((length-1)/3)+1)<<2] = '\0';
return 0;
}
static void createauth (const char *user, const char *pwd, char *buf, int len)
{
char tmp[128];
snprintf(tmp, sizeof(tmp), "%s:%s", user, pwd);
base64enc(tmp, strlen(tmp), &buf[strlen(buf)], len - strlen(buf));
}
#ifdef FEATURE_HTTP_TLS_ENABLE
static HTTPResult httpSslClose(HttpClientContext* context)
{
HttpClientSsl *ssl = (HttpClientSsl *)context->ssl;
/*context->clientCert = NULL;
context->caCert = NULL;
context->clientPk = NULL; let up level free it*/
if(ssl == NULL)
return HTTP_MBEDTLS_ERR;
mbedtls_ssl_close_notify(&(ssl->sslContext));
mbedtls_net_free(&(ssl->netContext));
mbedtls_x509_crt_free(&(ssl->caCert));
mbedtls_x509_crt_free(&(ssl->clientCert));
mbedtls_pk_free(&(ssl->pkContext));
mbedtls_ssl_free(&(ssl->sslContext));
mbedtls_ssl_config_free(&(ssl->sslConfig));
mbedtls_ctr_drbg_free(&(ssl->ctrDrbgContext));
mbedtls_entropy_free(&(ssl->entropyContext));
freeEc(ssl);
context->ssl = NULL;
return HTTP_OK;
}
static HTTPResult httpSslSend(mbedtls_ssl_context* sslContext, const char* buf, uint16_t len)
{
int32_t waitToSend = len;
int32_t hasSend = 0;
do
{
hasSend = mbedtls_ssl_write(sslContext, (unsigned char *)(buf + len - waitToSend), waitToSend);
if(hasSend > 0)
{
waitToSend -= hasSend;
}
else if(hasSend == 0)
{
return HTTP_OK;
}
else
{
HTTPINFO("http_client(ssl): send failed \n");
return HTTP_CONN;
}
}while(waitToSend>0);
return HTTP_OK;
}
static int httpSslNonblockRecv(void *netContext, uint8_t *buf, size_t len)
{
int ret;
int fd = ((mbedtls_net_context *)netContext)->fd;
if(fd < 0)
return HTTP_MBEDTLS_ERR;
ret = (int)recv(fd, buf, len, MSG_DONTWAIT);
if(ret<0){
if( errno == EPIPE || errno == ECONNRESET) {
return (MBEDTLS_ERR_NET_CONN_RESET);
}
if( errno == EINTR ) {
return (MBEDTLS_ERR_SSL_WANT_READ);
}
if(ret == -1 && errno == EWOULDBLOCK) {
return ret;
}
return (MBEDTLS_ERR_NET_RECV_FAILED);
}
return (ret);
}
static int sslRandom(void *p_rng, unsigned char *output, size_t output_len)
{
uint32_t rnglen = output_len;
uint8_t rngoffset = 0;
while (rnglen > 0) {
*(output + rngoffset) = (unsigned char)rand();
rngoffset++;
rnglen--;
}
return 0;
}
static void sslDebug(void *ctx, int level, const char *file, int line, const char *str)
{
//HTTPDBG("%s(%d):%s", file, line, str);
ECPLAT_PRINTF(UNILOG_HTTP, sslDebug, P_DEBUG, "%s(%d):%s", file, line, str);
}
static HTTPResult httpSslConn(HttpClientContext* context, char* host)
{
int value;
HttpClientSsl *ssl;
const char *custom = "https";
char port[10] = {0};
int authmode = MBEDTLS_SSL_VERIFY_NONE;
uint32_t flag = 0;
mbedtls_ssl_session* pSavedSession = NULL;
bool bDirectSaveSession = FALSE;
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_2, P_DEBUG, "before ssl context malloc:%d", xBytesTaskMalloced);
context->ssl = mallocEc(sizeof(HttpClientSsl));
if(context->ssl == NULL){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_0_0, P_VALUE, "no memory");
return HTTP_NO_MEMORY;
}
ssl = context->ssl;
/*
* 0. Initialize the RNG and the session data
*/
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold((int)DEBUG_LEVEL);
#endif
mbedtls_net_init(&ssl->netContext);
mbedtls_ssl_init(&ssl->sslContext);
mbedtls_ssl_config_init(&ssl->sslConfig);
mbedtls_x509_crt_init(&ssl->caCert);
mbedtls_x509_crt_init(&ssl->clientCert);
mbedtls_pk_init(&ssl->pkContext);
mbedtls_ctr_drbg_init(&ssl->ctrDrbgContext);
mbedtls_entropy_init(&ssl->entropyContext);
if((value = mbedtls_ctr_drbg_seed(&ssl->ctrDrbgContext,
mbedtls_entropy_func,
&ssl->entropyContext,
(const unsigned char*)custom,
strlen(custom))) != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_0, P_VALUE, "mbedtls_ctr_drbg_seed failed, value:-0x%x.", -value);
return HTTP_MBEDTLS_ERR;
}
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_3, P_DEBUG, "after ssl init:%d", xBytesTaskMalloced);
/*
* 0. Initialize certificates
*/
if(context->seclevel != 0){
if (NULL != context->caCert) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1, P_INFO, "STEP 0. Loading the CA root certificate ...");
authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
if (0 != (value = mbedtls_x509_crt_parse(&(ssl->caCert), (const unsigned char *)context->caCert, context->caCertLen))) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_0, P_VALUE, "failed ! value:-0x%x", -value);
return HTTP_MBEDTLS_ERR;
}else if(context->saveMem == 1){
freeEc(context->caCert);
context->caCert = NULL;
}
}
}
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_4, P_DEBUG, "after ca cert parse:%d", xBytesTaskMalloced);
/* Setup Client Cert/Key */
if(context->seclevel == 2){
if (context->clientCert != NULL && context->clientPk != NULL) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_1, P_INFO, "STEP 0. start prepare client cert ...");
value = mbedtls_x509_crt_parse(&(ssl->clientCert), (const unsigned char *) context->clientCert, context->clientCertLen);
if (value != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_2, P_VALUE, "failed! mbedtls_x509_crt_parse returned -0x%x\n", -value);
return HTTP_MBEDTLS_ERR;
}else if(context->saveMem == 1){
freeEc(context->clientCert);
context->clientCert = NULL;
flag = 1;
}
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_test_2, P_INFO, "context->clientPkLen=%d", context->clientPkLen);
value = mbedtls_pk_parse_key(&ssl->pkContext, (const unsigned char *) context->clientPk, context->clientPkLen, NULL, 0);
if (value != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_3, P_VALUE, "failed ! mbedtls_pk_parse_key returned -0x%x\n", -value);
return HTTP_MBEDTLS_ERR;
}else if(context->saveMem == 1){
freeEc(context->clientPk);
context->clientPk = NULL;
}
}
}
if(context->seclevel == 0){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_4, P_VALUE, "user set verify none");
authmode = MBEDTLS_SSL_VERIFY_NONE;
}
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_5, P_DEBUG, "after client cert parse:%d", xBytesTaskMalloced);
if(context->sni == 1){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_5, P_VALUE, "set sni");
ssl->sslContext.sni = 1;
}
if(context->ignore == 0){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_6, P_VALUE, "not ignore the crt's valid time");
ec_mbedtls_check_crt_valid = 1;
}else{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_1_7, P_VALUE, "ignore the crt's valid time");
ec_mbedtls_check_crt_valid = 0;
}
/*
* 1. Start the connection
*/
snprintf(port, sizeof(port), "%d", context->port);
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_2, P_INFO, "STEP 1. Connecting to PORT:%d",context->port);
if (0 != (value = mbedtls_net_connect(&ssl->netContext, host, port, MBEDTLS_NET_PROTO_TCP, context->pdpId))) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_2_1, P_VALUE, " failed ! mbedtls_net_connect returned -0x%x", -value);
return HTTP_MBEDTLS_ERR;
}
/*
* 2. Setup stuff
*/
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_3, P_INFO, "STEP 2. Setting up the SSL/TLS structure...");
if ((value = mbedtls_ssl_config_defaults(&(ssl->sslConfig), MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_3_1, P_VALUE, " failed! mbedtls_ssl_config_defaults returned -0x%x", -value);
return HTTP_MBEDTLS_ERR;
}
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_6, P_DEBUG, "after net connect:%d", xBytesTaskMalloced);
mbedtls_ssl_conf_max_version(&ssl->sslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
mbedtls_ssl_conf_min_version(&ssl->sslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
memcpy(&(ssl->crtProfile), ssl->sslConfig.cert_profile, sizeof(mbedtls_x509_crt_profile));
mbedtls_ssl_conf_authmode(&(ssl->sslConfig), authmode);
#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
if ((value = mbedtls_ssl_conf_max_frag_len(&(ssl->sslConfig), MBEDTLS_SSL_MAX_FRAG_LEN_4096)) != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_3_2, P_VALUE, " mbedtls_ssl_conf_max_frag_len returned -0x%x", -value);
return HTTP_MBEDTLS_ERR;
}
#endif
#if defined(MBEDTLS_X509_CRT_PARSE_C)
mbedtls_ssl_conf_cert_profile(&ssl->sslConfig, &ssl->crtProfile);
mbedtls_ssl_conf_ca_chain(&(ssl->sslConfig), &(ssl->caCert), NULL);
if(flag == 1) {
if ((value = mbedtls_ssl_conf_own_cert(&(ssl->sslConfig), &(ssl->clientCert), &(ssl->pkContext))) != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_3_3, P_VALUE, " failed! mbedtls_ssl_conf_own_cert returned -0x%x", -value);
return HTTP_MBEDTLS_ERR;
}
}
#endif
mbedtls_ssl_conf_session_tickets(&(ssl->sslConfig), MBEDTLS_SSL_SESSION_TICKETS_ENABLED);
if(context->ciphersuite[0] != 0xFFFF){
mbedtls_ssl_conf_ciphersuites(&(ssl->sslConfig), (const int *)(context->ciphersuite));
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_3_4, P_INFO, "conf ciphersuite 0x%x", context->ciphersuite[0]);
}
mbedtls_ssl_conf_rng(&(ssl->sslConfig), sslRandom, &(ssl->ctrDrbgContext));
mbedtls_ssl_conf_dbg(&(ssl->sslConfig), sslDebug, NULL);
#if defined(MBEDTLS_SSL_ALPN)
const char *alpn_list[] = { "http/1.1", NULL };
mbedtls_ssl_conf_alpn_protocols(&(ssl->sslConfig),alpn_list);
#endif
if(context->timeout_r > 0) {
uint32_t recvTimeout;
recvTimeout = context->timeout_r > MAX_TIMEOUT ? MAX_TIMEOUT * 1000 : context->timeout_r * 1000;
mbedtls_ssl_conf_read_timeout(&(ssl->sslConfig), recvTimeout);
}
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_7, P_DEBUG, "before ssl setup:%d", xBytesTaskMalloced);
if ((value = mbedtls_ssl_setup(&(ssl->sslContext), &(ssl->sslConfig))) != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_3_5, P_VALUE, " failed! mbedtls_ssl_setup returned -0x%x", -value);
return HTTP_MBEDTLS_ERR;
}
mbedtls_ssl_set_hostname(&(ssl->sslContext), host);
mbedtls_ssl_set_bio(&(ssl->sslContext), &(ssl->netContext), (mbedtls_ssl_send_t*)mbedtls_net_send, (mbedtls_ssl_recv_t*)mbedtls_net_recv, (mbedtls_ssl_recv_timeout_t*)mbedtls_net_recv_timeout);
/*
* 3. session resumption
*/
if(context->cache == 1 && gSslSessionContext != NULL){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_6, P_INFO, "has set cache and saved session");
if(strncasecmp((const CHAR*)gSslSessionContext->server, host, strlen(host)) == 0 && gSslSessionContext->port == context->port)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_6_1, P_VALUE, "current connect is the same");
pSavedSession = mallocEc(sizeof(mbedtls_ssl_session));
if(pSavedSession == NULL){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_6_1_0, P_VALUE, "no memory");
return HTTP_NO_MEMORY;
}
if( ( value = mbedtls_ssl_session_load( pSavedSession,
(const unsigned char*)gSslSessionContext->session,
gSslSessionContext->session_len ) ) == 0 )
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_6_2, P_INFO, "session_load success");
if( ( value = mbedtls_ssl_set_session( &(ssl->sslContext), pSavedSession ) ) != 0 )
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_6_4, P_VALUE, " failed! mbedtls_ssl_set_session returned -0x%x", -value );
}
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_6_5, P_VALUE, " failed! mbedtls_ssl_session_load returned -0x%x no session resumption", -value );
}
}
}
/*
* 4. Handshake
*/
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_8, P_DEBUG, "after ssl setup before handshake:%d", xBytesTaskMalloced);
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_4, P_INFO, "STEP 3. Performing the SSL/TLS handshake...");
while ((value = mbedtls_ssl_handshake(&(ssl->sslContext))) != 0) {
if ((value != MBEDTLS_ERR_SSL_WANT_READ) && (value != MBEDTLS_ERR_SSL_WANT_WRITE)) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_4_1, P_VALUE, "failed ! mbedtls_ssl_handshake returned -0x%x", -value);
return HTTP_MBEDTLS_ERR;
}
}
//ECPLAT_PRINTF(UNILOG_HTTP, sslMEM_9, P_DEBUG, "after handshake:%d", xBytesTaskMalloced);
/*
* 5. Verify the server certificate
*/
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_5, P_INFO, "STEP 4. Verifying peer X.509 certificate..");
flag = mbedtls_ssl_get_verify_result(&(ssl->sslContext));
if (flag != 0) {
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_5_1, P_VALUE, " failed ! verify result not confirmed.");
return HTTP_MBEDTLS_ERR;
}
/*
* 6. Save session for session resumption
*/
/* get size of the buffer needed */
if(context->cache == 1){
if(gSslSessionContext == NULL){//not save it yet
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_1, P_INFO, "not save session yet(%d)", sizeof(sslSessionContext));
gSslSessionContext = (sslSessionContext*)mallocEc(sizeof(sslSessionContext));
if(gSslSessionContext == NULL){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_1_0, P_VALUE, "no memory");
if(pSavedSession != NULL)
{
mbedtls_ssl_session_free(pSavedSession);
freeEc(pSavedSession);
pSavedSession = NULL;
}
return HTTP_NO_MEMORY;
}
memset(gSslSessionContext, 0, sizeof(sslSessionContext));
bDirectSaveSession = TRUE;
}else{//has saved session, to check whether the session is same with current session to be establish
if(strncasecmp((const CHAR*)gSslSessionContext->server, host, strlen(host)) == 0 && gSslSessionContext->port == context->port){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_2, P_VALUE, "current connect is the same");
if(ssl->sslContext.session->ticket_len != pSavedSession->ticket_len
|| memcmp(ssl->sslContext.session->ticket,pSavedSession->ticket, ssl->sslContext.session->ticket_len) != 0){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_3, P_VALUE, "server has give a new ticket, save it");
bDirectSaveSession = TRUE;
}
}else{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_4, P_VALUE, "current connect is diff save new session");
bDirectSaveSession = TRUE;
}
}
if(bDirectSaveSession == TRUE){
mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer(&(ssl->sslContext)),NULL, 0, &(gSslSessionContext->session_len) );
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_5, P_DEBUG, "new session need %d bytes", gSslSessionContext->session_len);
if(gSslSessionContext->session_len < SSL_SESSION_MAX_LEN){//this session can save in cache so save it. else not save it
/* actually save session data */
if( (value = mbedtls_ssl_session_save( mbedtls_ssl_get_session_pointer(&(ssl->sslContext)),
gSslSessionContext->session, gSslSessionContext->session_len,
&gSslSessionContext->session_len ) ) != 0 ){
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_6, P_VALUE, "failed ! mbedtls_ssl_session_saved returned -0x%x", -value );
}else{
ECPLAT_PRINTF(UNILOG_HTTP, httpSslConn_7_7, P_INFO, "new session get success");
strcpy(gSslSessionContext->server, host);
gSslSessionContext->port = context->port;
sslSaveSession();
}
}
}
}
if(pSavedSession != NULL)
{
mbedtls_ssl_session_free(pSavedSession);
freeEc(pSavedSession);
}
return HTTP_OK;
}
#endif
static HTTPResult httpSendInter(HttpClientContext* context, const char* buf, uint16_t len) //0 on success, err code on failure
{
int32_t waitToSend = len;
int32_t hasSend = 0;
fd_set writeFs;
struct timeval tv;
uint32_t preSelTime = 0,passedTime=0;
uint8_t ret=0;
tv.tv_sec = 0;
tv.tv_usec =context->timeout_s*1000000;
FD_ZERO(&writeFs);
if(context && context->socket >= 0)
FD_SET(context->socket, &writeFs);
do
{
tv.tv_usec -= (passedTime*1000);
//ECPLAT_PRINTF(UNILOG_HTTP, httpSend_0, P_DEBUG, 2, "passedTime: %d,tv_usec %d", passedTime,tv.tv_usec);
preSelTime=osKernelGetTickCount();
ret = select(context->socket + 1, NULL, &writeFs, NULL, &tv);
if(ret>0)
{
hasSend = send(context->socket, (buf + len - waitToSend), waitToSend, MSG_DONTWAIT);
ECPLAT_PRINTF(UNILOG_HTTP, httpSendInter_1, P_INFO, "%d bytes data has sent to server", hasSend);
//HTTPINFO("http_client: %d bytes data has sent to server\n", hasSend);
if(hasSend > 0)
{
waitToSend -= hasSend;
}
else if(hasSend == 0)
{
return HTTP_OK;
}
else
{
//HTTPINFO("http_client: send failed (errno:%d)\n",errno);
ECPLAT_PRINTF(UNILOG_HTTP, httpSendInter_2, P_VALUE, "http_client: send failed (errno:%d)", errno);
return HTTP_CONN;
}
ECPLAT_PRINTF(UNILOG_HTTP, httpSendInter_3, P_DEBUG, "preSelTime: %d,current tick %d", preSelTime,osKernelGetTickCount());
passedTime=(osKernelGetTickCount()-preSelTime)>0?(osKernelGetTickCount() - preSelTime):(0xFFFFFFFF - preSelTime + osKernelGetTickCount());
}
else//slelct return <=0 select timeout or error
{
return HTTP_CONN;
}
}while(waitToSend>0);
return HTTP_OK;
}
HTTPResult httpSend(HttpClientContext* context, const char* buf, uint16_t len) //0 on success, err code on failure
{
uint8_t ret=0;
if(!context->isHttps)
{
ret = httpSendInter(context, buf, len);
}
#ifdef FEATURE_HTTP_TLS_ENABLE
else
{
HttpClientSsl *ssl = (HttpClientSsl *)context->ssl;
ret = httpSslSend(&(ssl->sslContext), buf, len);
}
#endif
return ret;
}
HTTPResult httpRecv(HttpClientContext* context, char* buf, int32_t minLen, int32_t maxLen, int32_t* pReadLen) //0 on success, err code on failure
{
int32_t readLen = 0;
int ret = -1;
while (readLen < maxLen)
{
if(!context->isHttps)
{
if (readLen < minLen) {
ret = recv(context->socket, buf+readLen, minLen-readLen, 0);
if(ret == 0)
{
int mErr = sock_get_errno(context->socket);
if(socket_error_is_fatal(mErr))//maybe closed or reset by peer
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_1_1, P_VALUE, "recv return 0 fatal error return HTTP_CLOSED");
return HTTP_CLOSED;
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_1_2, P_INFO, "recv return 0 connect error");
return HTTP_CONN;
}
}
}
else
{
ret = recv(context->socket, buf+readLen, maxLen-readLen, MSG_DONTWAIT);
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_2, P_DEBUG, "recv [not blocking] return:%d", ret);
if(ret == -1)
{
int mErr = sock_get_errno(context->socket);
if(socket_error_is_fatal(mErr))//maybe closed or reset by peer
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_2_1, P_VALUE, "recv return -1 fatal error = %d return HTTP_CLOSED", mErr);
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_2_2, P_INFO, "recv [not blocking] errno = %d", mErr);
break;
}
}
}
}
#ifdef FEATURE_HTTP_TLS_ENABLE
else
{
HttpClientSsl *ssl = (HttpClientSsl *)context->ssl;
if (readLen < minLen) {
mbedtls_ssl_set_bio(&(ssl->sslContext), &(ssl->netContext), (mbedtls_ssl_send_t*)mbedtls_net_send, (mbedtls_ssl_recv_t*)mbedtls_net_recv, NULL);
ret = mbedtls_ssl_read(&(ssl->sslContext), (unsigned char *)(buf+readLen), minLen-readLen);
if(ret < 0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_3_1, P_VALUE, "mbedtls_ssl_read [blocking] return:-0x%x", -ret);
}
if(ret == 0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_3_2, P_VALUE, "mbedtls_ssl_read [blocking] return 0 connect error");
return HTTP_MBEDTLS_ERR;
}
}
else
{
mbedtls_ssl_set_bio(&(ssl->sslContext), &(ssl->netContext), (mbedtls_ssl_send_t*)mbedtls_net_send, (mbedtls_ssl_recv_t*)httpSslNonblockRecv, NULL);
ret = mbedtls_ssl_read(&(ssl->sslContext), (unsigned char*)(buf+readLen), maxLen-readLen);
if(ret < 0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_4, P_VALUE, "mbedtls_ssl_read [not blocking] return:-0x%x", -ret);
}
if(ret == -1)
{
int mErr = sock_get_errno(context->socket);
if(socket_error_is_fatal(mErr))//maybe closed or reset by peer
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_4_1, P_VALUE, "recv return -1 fatal error = %d return HTTP_CLOSED", mErr);
return HTTP_CLOSED;
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_4_2, P_VALUE, "mbedtls_ssl_read [not blocking] errno = %d", mErr);
break;
}
}
}
if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
return HTTP_CLOSED;
}
#endif
if (ret > 0)
{
readLen += ret;
}
else if ( ret == 0 )
{
break;
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_5, P_VALUE, "Connection error (recv returned -0x%x)", -ret);
*pReadLen = readLen;
if(context->isHttps)
{
return HTTP_MBEDTLS_ERR;
}
else
{
int mErr = sock_get_errno(context->socket);
if(socket_error_is_fatal(mErr))//maybe closed or reset by peer
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_6_1, P_VALUE, "recv return 0 fatal error return HTTP_CLOSED");
return HTTP_CLOSED;
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_6_2, P_INFO, "recv return 0 connect error");
return HTTP_CONN;
}
}
}
}
ECPLAT_PRINTF(UNILOG_HTTP, httpRecv_7, P_INFO, "Read %d bytes", readLen);
buf[readLen] = '\0'; // DS makes it easier to see what's new.
*pReadLen = readLen;
return HTTP_OK;
}
static HTTPResult prepareBuffer(HttpClientContext* context, char* sendBuf, int* cursor, char* buf, int len)
{
int copyLen;
int sendbufCursor = *cursor;
if(len == 0){
len = strlen(buf);
}
do {
if((CHUNK_SIZE - sendbufCursor) >= len) {
copyLen = len;
}else{
HTTPERR("send buffer overflow");
return HTTP_OVERFLOW;
}
memcpy(sendBuf + sendbufCursor, buf, copyLen);
sendbufCursor += copyLen;
len -= copyLen;
}while(len);
*cursor = sendbufCursor;
return HTTP_OK;
}
void httpFreeBuff(HTTPResult ret)
{
if(ret != 0)
{
if(httpSendBuf != NULL)
{
freeEc(httpSendBuf);
httpSendBuf = NULL;
}
if(httpSendBufTemp != NULL)
{
freeEc(httpSendBufTemp);
httpSendBufTemp = NULL;
}
}
}
static HTTPResult httpSendHeader(HttpClientContext* context, const char * url, HTTP_METH method, HttpClientData * data)
{
char scheme[8];
uint16_t port;
char host[MAXHOST_SIZE];
char path[MAXPATH_SIZE];
HTTPResult ret = HTTP_OK;
int bufCursor = 0;
memset(host, 0, MAXHOST_SIZE);
memset(path, 0, MAXPATH_SIZE);
context->method = method;
HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
if(res != HTTP_OK) {
HTTPERR("parseURL returned %d", res);
return res;
}
httpSendBuf = mallocEc(CHUNK_SIZE);
if(httpSendBuf == NULL){
ECPLAT_PRINTF(UNILOG_HTTP, httpSendHeader_0_0, P_VALUE, "no memory");
return HTTP_NO_MEMORY;
}
memset(httpSendBuf, 0, CHUNK_SIZE);
httpSendBufTemp = mallocEc(TEMPBUF_SIZE);
if(httpSendBufTemp == NULL){
ECPLAT_PRINTF(UNILOG_HTTP, httpSendHeader_0_1, P_VALUE, "no memory");
freeEc(httpSendBuf);
httpSendBuf = NULL;
return HTTP_NO_MEMORY;
}
const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_HEAD)?"HEAD":(method==HTTP_DELETE)?"DELETE":"";
snprintf(httpSendBufTemp, TEMPBUF_SIZE, "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
// send authorization
if (context->basicAuthUser && context->basicAuthPassword) {
memset(httpSendBufTemp, 0, TEMPBUF_SIZE);
HTTPINFO("send auth (if defined)");
strcpy(httpSendBufTemp, "Authorization: Basic ");
createauth(context->basicAuthUser, context->basicAuthPassword, httpSendBufTemp+strlen(httpSendBufTemp), TEMPBUF_SIZE-strlen(httpSendBufTemp));
strcat(httpSendBufTemp, "\r\n");
HTTPINFO(" (%s,%s) => (%s)", context->basicAuthUser, context->basicAuthPassword, httpSendBufTemp);
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
}
// Send custom header
if(context->custHeader) {
snprintf(httpSendBufTemp, TEMPBUF_SIZE, "%s\r\n", context->custHeader);
HTTPINFO("httpSendHeader custheader:{%s}", httpSendBufTemp);
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
}
// set range
if(data->isRange)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSendHeader_1, P_INFO, "Range:bytes=%d-%d",data->rangeHead, data->rangeTail);
if(data->rangeTail == -1){
snprintf(httpSendBufTemp, TEMPBUF_SIZE, "Range:bytes=%d-\r\n", data->rangeHead);
}else{
snprintf(httpSendBufTemp, TEMPBUF_SIZE, "Range:bytes=%d-%d\r\n", data->rangeHead, data->rangeTail);
}
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
}
//Send default headers
strcpy(httpSendBufTemp, "Connection: Keep-Alive\r\n");
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
snprintf(httpSendBufTemp, TEMPBUF_SIZE, "Content-Length: %d\r\n", data->postBufLen);
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
if(data->postContentType != NULL)
{
snprintf(httpSendBufTemp, TEMPBUF_SIZE, "Content-Type: %s\r\n", data->postContentType);
ret = prepareBuffer(context, httpSendBuf, &bufCursor, httpSendBufTemp, strlen(httpSendBufTemp));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
}
//Close headers
ret = prepareBuffer(context, httpSendBuf, &bufCursor, "\r\n", strlen("\r\n"));
httpFreeBuff(ret);
OVERFLOW_ERR(ret);
HTTPINFO("httpSendHeader send head:%s, headlen:%d", httpSendBuf,strlen(httpSendBuf));
if(!context->isHttps){
ret = httpSendInter(context, httpSendBuf, strlen(httpSendBuf));
httpFreeBuff(ret);
CHECK_CONN_ERR(ret);
}
#ifdef FEATURE_HTTP_TLS_ENABLE
else {
HttpClientSsl *ssl = (HttpClientSsl *)context->ssl;
ret = httpSslSend(&(ssl->sslContext), httpSendBuf, strlen(httpSendBuf));
httpFreeBuff(ret);
CHECK_CONN_ERR(ret);
}
#endif
httpFreeBuff((HTTPResult)1);
return ret;
}
static HTTPResult httpSendUserdata(HttpClientContext* context, HttpClientData * data)
{
HTTPResult ret = HTTP_OK;
//ECPLAT_PRINTF(UNILOG_HTTP, httpSendUserdata_1, P_DEBUG, 0, "begin send content");
if(data->postBuf && data->postBufLen)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSendUserdata_2, P_INFO, "data->postBufLen=%d",data->postBufLen);
if(!context->isHttps)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpSendUserdata_3, P_DEBUG, "data->postBuf=%s",(uint8_t*)data->postBuf);
ret = httpSendInter(context, data->postBuf, data->postBufLen);
CHECK_CONN_ERR(ret);
}
#ifdef FEATURE_HTTP_TLS_ENABLE
else
{
HttpClientSsl *ssl = (HttpClientSsl *)context->ssl;
ret = httpSslSend(&(ssl->sslContext), data->postBuf, data->postBufLen);
CHECK_CONN_ERR(ret);
}
#endif
}
return ret;
}
static HTTPResult check_timeout_ret(HTTPResult ret){
AP_PLAT_COMMON_BSS static uint8_t count = 0;
if(ret == HTTP_OK){
count = 0;
}else if(ret == HTTP_CONN ){
if(count < 3){
ECPLAT_PRINTF(UNILOG_HTTP, check_timeout_ret_1, P_VALUE, "wait %d x 20s", count);
count += 1;
}else{
ECPLAT_PRINTF(UNILOG_HTTP, check_timeout_ret_2, P_VALUE, "give up return HTTP_TIMEOUT");
ret = HTTP_REQ_TIMEOUT;
}
}
return ret;
}
static HTTPResult httpParseContent(HttpClientContext* context, char * buf, int32_t trfLen, HttpClientData * data)
{
int32_t crlfPos = 0;
HTTPResult ret = HTTP_OK;
int maxlen;
int total = 0;
int templen = 0;
AP_PLAT_COMMON_BSS static int seqNum = 0;
//Receive data
//ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_1, P_DEBUG, 0, "begin parse content");
data->isMoreContent = TRUE;
if(data->recvContentLength == -1 && data->isChunked == FALSE)
{
while(true)
{
if(total + trfLen < data->respBufLen - 1)
{
memcpy(data->respBuf + total, buf, trfLen);
total += trfLen;
data->respBuf[total] = '\0';
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_2, P_DEBUG, "all data are here");
}
else
{
memcpy(data->respBuf + total, buf, data->respBufLen - 1 - total);
data->respBuf[data->respBufLen-1] = '\0';
data->blockContentLen = data->respBufLen-1;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_3, P_DEBUG, "still has more data on the way");
return HTTP_MOREDATA;
}
maxlen = MIN(CHUNK_SIZE - 1, data->respBufLen - 1 - total);
ret = httpRecv(context, buf, 1, maxlen, &trfLen);
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_4, P_DEBUG, "receive data len:%d, total:%d", trfLen, total);
if(ret != HTTP_OK)
{
data->blockContentLen = total;
data->isMoreContent = false;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_5, P_DEBUG, "ret:%d", ret);
return ret;
}
if(trfLen == 0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_6, P_DEBUG, "no more data read");
data->isMoreContent = false;
return HTTP_OK;
}
}
}
while(true) {
int32_t readLen = 0;
if( data->isChunked && data->needObtainLen <= 0) {//content is chunked code and first package
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_7, P_VALUE, "content is chunked code");
//Read chunk header
bool foundCrlf;
do {
foundCrlf = false;
crlfPos=0;
buf[trfLen]=0;
if(trfLen >= 2) {
for(; crlfPos < trfLen - 2; crlfPos++) {
if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) {
foundCrlf = true;
break;
}
}
}
if(!foundCrlf) { //Try to read more
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_7_2, P_WARNING, "no find crlf to read more");
maxlen = MIN(CHUNK_SIZE-trfLen-1, data->respBufLen-1-total);
if( trfLen < maxlen ) {
int32_t newTrfLen = 0;
ret = httpRecv(context, buf + trfLen, 0, maxlen, &newTrfLen);
trfLen += newTrfLen;
CHECK_CONN_ERR(ret);
continue;
} else {
PRTCL_ERR();
}
}
} while(!foundCrlf);
buf[crlfPos] = '\0';
int n = sscanf(buf, "%x", &readLen);
data->needObtainLen = readLen;
data->recvContentLength += readLen;
if(n!=1) {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_7_1, P_WARNING, "Could not read chunk length");
PRTCL_ERR();
}
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
trfLen -= (crlfPos + 2);
if( readLen == 0 ) {
//Last chunk
data->isMoreContent = false;
break;
}
} else {
readLen = data->needObtainLen;
}
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_8, P_INFO, "need to obtaining %d bytes trfLen=%d", readLen,trfLen);
do {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9, P_VALUE, "trfLen=%d, readLen=%d", trfLen, readLen);
templen = MIN(trfLen, readLen);
if(total+templen < data->respBufLen - 1){
memcpy(data->respBuf+total, buf, templen);
total += templen;
data->respBuf[total] = '\0';
data->needObtainLen -= templen;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_1, P_VALUE, "templen=%d data->needObtainLen=%d", templen,data->needObtainLen);
} else {
if(data->respBufLen -1 < trfLen){
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_2_1, P_WARNING, "data->respBufLen=%d is too small, data has overflowed", data->respBufLen);
}
memcpy(data->respBuf + total, buf, data->respBufLen - 1 - total);
data->respBuf[data->respBufLen - 1] = '\0';
data->needObtainLen -= data->respBufLen - 1 - total;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_2, P_VALUE, "data->needObtainLen=%d total=%d", data->needObtainLen,total);
if(readLen > trfLen){
data->blockContentLen = data->respBufLen -1;
seqNum += 1;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_3, P_VALUE, "return 12 moredata data->blockContentLen=%d, seqNum=%d", data->blockContentLen, seqNum);
return HTTP_MOREDATA;
}else{
total += templen;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_4, P_VALUE, "templen=%d total=%d", templen,total);
}
}
if( trfLen >= readLen ) {
memmove(buf, &buf[readLen], trfLen - readLen);
trfLen -= readLen;
readLen = 0;
data->needObtainLen = 0;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_5, P_VALUE, "trfLen=%d data->needObtainLen and readLen set 0", trfLen);
} else {
readLen -= trfLen;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_6, P_VALUE, "readLen=%d", readLen);
}
if(readLen) {
maxlen = MIN(MIN(CHUNK_SIZE-1, data->respBufLen-1-total),readLen);
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_7, P_VALUE, "to read maxlen=%d", maxlen);
ret = httpRecv(context, buf, 1, maxlen, &trfLen);
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_9_8, P_VALUE, "httpRecv return: %d,trfLen:%d", ret,trfLen);
ret = check_timeout_ret(ret);
CHECK_ERR(ret);
}
} while(readLen);
if( data->isChunked ) {
if(trfLen < 2) {
int32_t newTrfLen;
//Read missing chars to find end of chunk
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_10, P_VALUE, "search end of chunk");
maxlen = MIN(CHUNK_SIZE-trfLen-1, data->respBufLen-1-total);
ret = httpRecv(context, buf + trfLen, 2 - trfLen, maxlen, &newTrfLen);
CHECK_CONN_ERR(ret);
trfLen += newTrfLen;
}
if( (buf[0] != '\r') || (buf[1] != '\n') ) {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_10_1, P_WARNING, "Format error");
PRTCL_ERR();
}
memmove(buf, &buf[2], trfLen - 2);
trfLen -= 2;
} else {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_11, P_VALUE, "no more content");
data->isMoreContent = false;
break;
}
}
ECPLAT_PRINTF(UNILOG_HTTP, httpParseContent_12, P_VALUE, "all content over, seqNum=%d", seqNum);
data->blockContentLen = total;
return ret;
}
static HTTPResult httpParseHeader(HttpClientContext* context, char * buf, int32_t trfLen, HttpClientData * data)
{
HTTPResult ret;
int32_t crlfPos = 0;
int temp1 = 0, temp2 = 0;
int headerBufLen = data->headerBufLen;
char *headerBuf = data->headerBuf;
memset(headerBuf, 0, headerBufLen);
data->recvContentLength = -1;
data->isChunked = false;
char* crlfPtr = strstr(buf, "\r\n");
if( crlfPtr == NULL) {
PRTCL_ERR();
}
crlfPos = crlfPtr - buf;
int32_t httpheadPos = 0;
char* httpPtr = strstr(buf, "HTTP");
if(httpPtr == NULL) {
PRTCL_ERR();
}
httpheadPos = httpPtr - buf;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_0_1, P_INFO, "httpheadPos=%d,crlfPos=%d", httpheadPos,crlfPos);
memcpy(headerBuf, buf+httpheadPos, crlfPos - httpheadPos + 2);
headerBuf += crlfPos - httpheadPos + 2;
headerBufLen -= crlfPos - httpheadPos + 2;
buf[crlfPos] = '\0';
//char temp[36] = {0};
//strncpy(temp, buf, 36);
//Parse HTTP response
if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &(context->httpResponseCode)) != 1 ) {
//Cannot match string, error
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_1, P_VALUE, "Not a correct HTTP answer");
PRTCL_ERR();
}
if( (context->httpResponseCode < 200) || (context->httpResponseCode >= 400) ) {
//Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_2, P_INFO, "Response code %d", context->httpResponseCode);
}
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_2_1, P_INFO, "context->httpResponseCode = %d", context->httpResponseCode);
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
trfLen -= (crlfPos + 2);
//Now get headers
while( true ) {
char *colonPtr, *keyPtr, *valuePtr;
int keyLen, valueLen;
crlfPtr = strstr(buf, "\r\n");
if(crlfPtr == NULL) {
if( trfLen < (CHUNK_SIZE - 1) )
{
int32_t newTrfLen = 0;
ret = httpRecv(context, buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
trfLen += newTrfLen;
buf[trfLen] = '\0';
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_3, P_VALUE, "trfLen = %d, new recv:%d", trfLen, newTrfLen);
CHECK_ERR(ret);
continue;
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_4, P_VALUE, "trfLen = %d > CHUNK_SIZE",trfLen);
PRTCL_ERR();
}
}
crlfPos = crlfPtr - buf;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_4_1, P_VALUE, "crlfPos = %d...trfLen=%d",crlfPos,trfLen);
if(crlfPos == 0) { //End of headers
memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
trfLen -= 2;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_5, P_VALUE, "End of headers,trfLen=%d",trfLen);
break;
}
colonPtr = strstr(buf, ": ");
if (colonPtr) {
if (headerBufLen >= crlfPos + 2) {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_5_1, P_VALUE, "headerBufLen=%d crlfPos=%d",headerBufLen,crlfPos);
/* copy response header to caller buffer */
memcpy(headerBuf, buf, crlfPos + 2);
headerBuf += crlfPos + 2;
headerBufLen -= crlfPos + 2;
}
keyLen = colonPtr - buf;
valueLen = crlfPtr - colonPtr - strlen(": ");
keyPtr = buf;
valuePtr = colonPtr + strlen(": ");
//printf("Read header : %.*s: %.*s\r\n", keyLen, keyPtr, valueLen, valuePtr);
if (0 == strncasecmp(keyPtr, "Content-Length", keyLen)) {
sscanf(valuePtr, "%d[^\r]", &(data->recvContentLength));
data->needObtainLen = data->recvContentLength;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_6, P_VALUE, "data->needObtainLen=%d",data->needObtainLen);
} else if (0 == strncasecmp(keyPtr, "Transfer-Encoding", keyLen)) {
if (0 == strncasecmp(valuePtr, "Chunked", valueLen)) {
data->isChunked = true;
data->recvContentLength = 0;
data->needObtainLen = 0;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_7, P_VALUE, "data->isChunked = true");
}
} else if (0 == strncasecmp(keyPtr, "Content-Range", keyLen)) {
sscanf(valuePtr, "%*[^/]/%d[^\r]", &(data->contentRange));
sscanf(valuePtr, "%*[^ ] %d-[^\\-]", &(temp1));
sscanf(valuePtr, "%*[^\\-]-%d[^/]", &(temp2));
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_6_1, P_VALUE, "data->contentRange=%d,current head=%d tail=%d",data->contentRange,temp1,temp2);
}
memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
trfLen -= (crlfPos + 2);
} else {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_8, P_WARNING, "Could not parse header");
PRTCL_ERR();
}
}// get headers
ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_9, P_VALUE, "Completed HTTP header parse");
if(context->method == HTTP_HEAD)
return HTTP_OK;
else{
//ECPLAT_PRINTF(UNILOG_HTTP, httpParseHeader_10, P_INFO, "continue parse content, trfLen=%d",trfLen);
ret = httpParseContent(context, buf, trfLen, data);
}
return ret;
}
static HTTPResult httpConnectTimeout(int32_t connectFd, uint32_t timeout)
{
fd_set writeSet;
fd_set errorSet;
FD_ZERO(&writeSet);
FD_ZERO(&errorSet);
FD_SET(connectFd,&writeSet);
FD_SET(connectFd,&errorSet);
struct timeval tv;
tv.tv_sec = timeout;
tv.tv_usec = 0;
INT32 ret;
ret = select(connectFd+1, NULL, &writeSet, &errorSet, &tv);
if(ret < 0)
{
return HTTP_CONN;
}
else if(ret == 0)
{
return HTTP_TIMEOUT;
}
else
{
if(FD_ISSET(connectFd, &errorSet))
{
int mErr = sock_get_errno(connectFd);
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectTimeout_2, P_VALUE, "select error fd set get errno=%d", mErr);
if(mErr)
{
return HTTP_CONN;
}
}
else if(FD_ISSET(connectFd, &writeSet))
{
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectTimeout_3, P_VALUE, "connect success");
}
}
return HTTP_OK;
}
static HTTPResult httpConnectSocket(int32_t socket,struct sockaddr *addr, int32_t addrlen)
{
HTTPResult ret = HTTP_OK;
int32_t errCode;
if(connect(socket,addr,addrlen) == 0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectSocket_1, P_INFO, "httpConnectSocket connect success");
}
else
{
errCode = sock_get_errno(socket);
if(errCode == EINPROGRESS)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectSocket_2, P_INFO, "httpConnectSocket connect is ongoing");
ret = httpConnectTimeout(socket, 25);//from 10s to 25s
if(ret == 0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectSocket_3, P_VALUE, "httpConnectSocket connect success");
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectSocket_4, P_WARNING, "httpConnectSocket connect fail,error code %d", errCode);
if(socket_error_is_fatal(errCode))
{
ret = HTTP_CLOSED;
}
}
}
else
{
ECPLAT_PRINTF(UNILOG_HTTP, httpConnectSocket_5, P_WARNING, "httpConnectSocket connect fail %d",errCode);
ret = HTTP_CONN;
}
}
return ret;
}
static HTTPResult httpConn(HttpClientContext* context, char* host)
{
HTTPResult ret=HTTP_OK;
//struct timeval timeout_send;
struct timeval timeout_recv;
struct addrinfo hints, *addr_list, *p;
char port[10] = {0};
int retVal = 0;
//timeout_send.tv_sec = context->timeout_s > MAX_TIMEOUT ? MAX_TIMEOUT : context->timeout_s;
//timeout_send.tv_usec = 0;
timeout_recv.tv_sec = context->timeout_r > MAX_TIMEOUT ? MAX_TIMEOUT : context->timeout_r;
timeout_recv.tv_usec = 0;
memset( &hints, 0, sizeof( hints ) );
//hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
snprintf(port, sizeof(port), "%d", context->port) ;
if (getaddrinfo( host, port , &hints, &addr_list ) != 0 ) {
ECPLAT_PRINTF(UNILOG_HTTP, httpConn_1, P_WARNING, "HTTP connect unresolved dns");
return HTTP_DNS;
}
//try address one by one until sucess
for ( p = addr_list; p != NULL; p = p->ai_next ) {
context->socket = (int) socket( p->ai_family, p->ai_socktype,p->ai_protocol);
if ( context->socket < 0 ) {
ret = HTTP_SOCKET_FAIL;
continue;//try new one
}
retVal = bind_cid(context->socket, context->pdpId);
if(retVal == -1){
ret = HTTP_BIND_FAIL;
continue;//try new one
}
/* set timeout for both tx removed since lwip not support tx timeout */
//if ( context->timeout_s > 0) {
// setsockopt(context->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout_send, sizeof(timeout_send));
//}
/* set timeout for both rx */
if ( context->timeout_r > 0) {
setsockopt(context->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout_recv, sizeof(timeout_recv));;
}
#ifdef HTTP_CONNECT_TIMEOUT_EN
int32_t flags = fcntl( context->socket, F_GETFL, 0);
if(flags < 0)
{
ECPLAT_PRINTF(UNILOG_ATCMD_SOCK, httpConn_3, P_ERROR, "httpCreateSocket get file cntrl flags fail");
close(context->socket);
context->socket = -1;
continue;//try new one
}
fcntl(context->socket, F_SETFL, flags|O_NONBLOCK); //set socket as nonblock for connect timeout
if ( httpConnectSocket( context->socket, p->ai_addr, (int32_t)p->ai_addrlen ) == HTTP_OK ) {
ECPLAT_PRINTF(UNILOG_HTTP, httpConn_2, P_INFO, "HTTP connect success");
ret = HTTP_OK;//connect success
fcntl(context->socket, F_SETFL, flags&~O_NONBLOCK); //connect success recover to block mode
break;
}
fcntl(context->socket, F_SETFL, flags&~O_NONBLOCK); //connect fail recover to block mode
#else
if ( connect( context->socket, p->ai_addr, (int)p->ai_addrlen ) == 0 ) {
ret = HTTP_OK;//connect success
break;
}
#endif
close( context->socket );
context->socket = -1;
ret = HTTP_SOCKET_FAIL;
}
freeaddrinfo( addr_list );
return ret;
}
/****************************************************************************************************************************
****************************************************************************************************************************
* EXTERNAL/GLOBAL FUNCTION
****************************************************************************************************************************
****************************************************************************************************************************/
#define __DEFINE_GLOBAL_FUNCTION__//just for easy to find this position
void basicAuth(HttpClientContext* context, const char* user, const char* password) //Basic Authentification
{
if (context->basicAuthUser)
freeEc(context->basicAuthUser);
context->basicAuthUser = (char *)mallocEc(strlen(user)+1);
strcpy(context->basicAuthUser, user);
if (context->basicAuthPassword)
freeEc(context->basicAuthPassword);
context->basicAuthPassword = (char *)mallocEc(strlen(password)+1);
strcpy(context->basicAuthPassword, password);//not free yet!!!
}
void custHeader(HttpClientContext* context, char *header)
{
context->custHeader = header;
}
HTTPResult httpParseURL(HttpClientContext* context, char* url)
{
char scheme[8]= {0};
uint16_t port;
char host[MAXHOST_SIZE] = {0};
char path[MAXPATH_SIZE] = {0};
if(context == NULL)
return HTTP_INTERNAL;
//First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
if(res != HTTP_OK) {
ECPLAT_PRINTF(UNILOG_HTTP, httpParseURL_0, P_INFO, "parseURL fail");
return res;
}
if(strncasecmp((const char*)scheme, "https", strlen("https")) == 0)
context->isHttps = true;
else
context->isHttps = false;
ECPLAT_PRINTF(UNILOG_HTTP, httpParseURL_1, P_INFO, "isHttps=%d", context->isHttps);
return HTTP_OK;
}
HTTPResult httpConnect(HttpClientContext* context, const char* url) //Execute request
{
HTTPResult ret = HTTP_INTERNAL;
char scheme[8]= {0};
uint16_t port;
char host[MAXHOST_SIZE] = {0};
char path[MAXPATH_SIZE] = {0};
HTTPINFO("httpConnect parse url: [%s]", url);
//First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
if(res != HTTP_OK) {
HTTPERR("parseURL returned %d", res);
return res;
}
if(strncasecmp((const char*)scheme, "https", strlen("https")) == 0)
context->isHttps = TRUE;
else
context->isHttps = FALSE;
if(port == 0) {
if(context->isHttps)
port = 443;
else
port = 80;
}
context->port = port;
HTTPDBG("httpConnect Scheme: %s", scheme);
HTTPDBG("httpConnect Host: %s", host);
HTTPDBG("httpConnect Port: %d", port);
HTTPDBG("httpConnect Path: %s", path);
if(!context->isHttps) {
ret = httpConn(context, host);
}
#ifdef FEATURE_HTTP_TLS_ENABLE
else {
ret = httpSslConn(context, host);
if(HTTP_OK == ret) {
HttpClientSsl *ssl = (HttpClientSsl *)context->ssl;
context->socket = ssl->netContext.fd;
}
}
#endif
return ret;
}
HTTPResult httpSendRequest(HttpClientContext* context, const char* url, HTTP_METH method, HttpClientData * data)
{
HTTPResult ret = HTTP_CONN;
if(context->socket <0)
{
return ret;
}
ret = httpSendHeader(context, url, method, data);
if(ret != HTTP_OK)
return ret;
if(method == HTTP_GET || method == HTTP_POST)
{
ret = httpSendUserdata(context, data);
}
HTTPDBG("httpSendRequest ret:%d",ret);
return ret;
}
HTTPResult httpClose(HttpClientContext* context)
{
HTTPResult ret = HTTP_OK;
if(!context->isHttps)
{
if(context->socket >= 0)
close(context->socket);
}
#ifdef FEATURE_HTTP_TLS_ENABLE
else
{
ret = httpSslClose(context);
}
#endif
/*if(context->basicAuthUser){
freeEc(context->basicAuthUser);
if(context->basicAuthPassword)
freeEc(context->basicAuthPassword);let up level free it*/
context->socket = -1;
HTTPDBG("httpClose");
ECPLAT_PRINTF(UNILOG_HTTP, httpClose, P_VALUE, "httpClose,ret=%d",ret);
return ret;
}
AP_PLAT_COMMON_BSS char *httpRecvRespBuf = NULL;
HTTPResult httpRecvResponse(HttpClientContext* context, HttpClientData * data)
{
//Receive response
HTTPResult ret = HTTP_CONN;
int32_t trfLen = 0;
//char buf[CHUNK_SIZE+2] = {0};
HTTPDBG("Receiving response");
ECPLAT_PRINTF(UNILOG_HTTP, httpRecvResponse_1, P_DEBUG, "Receiving response");
if(context->socket < 0)
return ret;
httpRecvRespBuf = mallocEc(CHUNK_SIZE);
if(httpRecvRespBuf == NULL)
return HTTP_NO_MEMORY;
memset(httpRecvRespBuf, 0, (CHUNK_SIZE));
if(data->isMoreContent)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecvResponse_2, P_INFO, "data->isMoreContent is true continue parseContent");
data->respBuf[0] = '\0';
ret = httpParseContent(context, httpRecvRespBuf, trfLen, data);
}
else
{
ret = httpRecv(context, httpRecvRespBuf, 1, CHUNK_SIZE - 1, &trfLen); // recommended by Rob Noble to avoid timeout wait
if(ret != HTTP_OK && trfLen==0)
{
ECPLAT_PRINTF(UNILOG_HTTP, httpRecvResponse_3_1, P_INFO, "no read data meet error");
if(httpRecvRespBuf != NULL)
{
freeEc(httpRecvRespBuf);
httpRecvRespBuf = NULL;
}
return ret;
}
ECPLAT_PRINTF(UNILOG_HTTP, httpRecvResponse_3, P_INFO, "has read %d bytes", trfLen);
httpRecvRespBuf[trfLen] = '\0';
if(trfLen)
{
ret = httpParseHeader(context, httpRecvRespBuf, trfLen, data);
}
}
if(httpRecvRespBuf != NULL)
{
freeEc(httpRecvRespBuf);
httpRecvRespBuf = NULL;
}
return ret;
}
HTTPResult httpGet(HttpClientContext* context, const char* url, HttpClientData * data, int timeout) //Blocking
{
context->timeout_s = timeout;
HTTPResult ret = HTTP_CONN;
ret = httpConnect(context, url);
if(ret == HTTP_OK)
{
ret = httpSendRequest(context, url, HTTP_GET, data);
if(ret == HTTP_OK)
{
ret = httpRecvResponse(context, data);
}
}
return ret;
}
HTTPResult httpPost(HttpClientContext* context, const char* url, HttpClientData * data, int timeout) //Blocking
{
context->timeout_s = timeout;
HTTPResult ret = HTTP_CONN;
ret = httpConnect(context, url);
if(ret == HTTP_OK)
{
ret = httpSendRequest(context, url, HTTP_POST, data);
if(ret == HTTP_OK)
{
ret = httpRecvResponse(context, data);
}
}
return ret;
}