1725 lines
64 KiB
C
1725 lines
64 KiB
C
|
/* 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|