Add toolchain and mbtk source

Change-Id: Ie12546301367ea59240bf23d5e184ad7e36e40b3
diff --git a/mbtk/mbtk_lib/src/mbtk_http.c b/mbtk/mbtk_lib/src/mbtk_http.c
new file mode 100755
index 0000000..e63064e
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_http.c
@@ -0,0 +1,1361 @@
+/*************************************************************
+Description:
+    MBTK HTTP c file.
+Author:
+    LiuBin
+Date:
+    2020/4/30 13:51:42
+*************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "mbtk_http_base.h"
+#include "mbtk_http.h"
+#include "mbtk_http_chunks.h"
+
+/*************************************************************
+    Constants and Macros
+*************************************************************/
+
+/*************************************************************
+    Variables:local
+*************************************************************/
+static mbtk_http_handle_t http_handles[HTTP_HANDLE_MAX] =
+{
+    {
+        .id = -1,
+        .data_cb = NULL,
+        .session_cnt = 0,
+        .session = {NULL}
+    }
+};
+
+/*************************************************************
+    Variables:public
+*************************************************************/
+
+
+/*************************************************************
+    Local Function Declaration
+*************************************************************/
+
+
+/*************************************************************
+    Local Function Definitions
+*************************************************************/
+static void http_session_free(mbtk_http_session_t *session)
+{
+    if(session)
+    {
+        if(session->req.header_cnt > 0)
+        {
+            int index;
+            for(index = 0; index < HTTP_REQUEST_HEADER_MAX; index++)
+            {
+                if(session->req.req_h[index] != NULL)
+                {
+                    if(session->req.req_h[index]->value)
+                        free(session->req.req_h[index]->value);
+                    free(session->req.req_h[index]);
+                    session->req.req_h[index] = NULL;
+                }
+            }
+            session->req.header_cnt = 0;
+        }
+
+        if(session->req.content)
+        {
+            free(session->req.content);
+            session->req.content = NULL;
+        }
+
+        if(session->rsp.header_cnt > 0)
+        {
+            int index;
+            for(index = 0; index < HTTP_REQUEST_HEADER_MAX; index++)
+            {
+                if(session->rsp.rsp_h[index] != NULL)
+                {
+                    if(session->rsp.rsp_h[index]->value)
+                        free(session->rsp.rsp_h[index]->value);
+                    free(session->rsp.rsp_h[index]);
+                    session->rsp.rsp_h[index] = NULL;
+                }
+            }
+            session->rsp.header_cnt = 0;
+        }
+
+        free(session);
+    }
+}
+
+static int http_session_close(mbtk_http_session_t *session)
+{
+    if(session)
+    {
+        if(session->sock_fd > 0)
+        {
+            if(mbtk_http_close(session->sock_fd))
+            {
+                LOGE("mbtk_http_close() fail.");
+                return -1;
+            }
+            session->sock_fd = -1;
+        }
+
+        session->state = HTTP_SESSION_STATE_NON;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+static bool http_handle_check(int handle_id)
+{
+    if(handle_id < 0 || handle_id >= HTTP_HANDLE_MAX
+       || http_handles[handle_id].id < 0)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static bool http_session_check(int handle_id, int session_id)
+{
+    if(handle_id < 0 || handle_id >= HTTP_HANDLE_MAX
+       || http_handles[handle_id].id < 0
+       || http_handles[handle_id].id != handle_id)
+    {
+        return FALSE;
+    }
+
+    if(session_id < 0 || session_id >= HTTP_SESSION_MAX
+       || http_handles[handle_id].session[session_id] == NULL
+       || http_handles[handle_id].session[session_id]->id != session_id)
+    {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static bool http_is_space_char(char ch)
+{
+    if(ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n')
+        return TRUE;
+
+    return FALSE;
+}
+
+static bool http_str_empty(char *str)
+{
+    if(str == NULL || strlen(str) == 0)
+        return TRUE;
+
+    return FALSE;
+}
+
+
+static int http_url_parse
+(
+    void* url,
+    void *host,
+    void *uri,
+    int *port,
+    bool *is_ssl
+)
+{
+    if(strlen(url) == 0)
+    {
+        return -1;
+    }
+    char *url_ptr = (char*)url;
+    char *host_ptr = (char*)host;
+
+    LOGI("URL[%d]:%s",strlen(url_ptr),url_ptr);
+
+    if(!memcmp(url_ptr,"https://",8))
+    {
+        *is_ssl = TRUE;
+        url_ptr += 8;
+    }
+    else if(!memcmp(url_ptr,"http://",7))
+    {
+        *is_ssl = FALSE;
+        url_ptr += 7;
+    }
+    else
+    {
+        *is_ssl = FALSE;
+    }
+
+    // ptr point to host.
+    while(*url_ptr)
+    {
+        if(*url_ptr == ':' || *url_ptr == '/') // Host end
+            break;
+        if(http_is_space_char(*url_ptr))
+        {
+            url_ptr++;
+            continue;
+        }
+        *host_ptr++ = *url_ptr++;
+    }
+
+    // "www.baidu.com"
+    if(*url_ptr == '\0')   // No port and uri
+    {
+        if(*is_ssl)
+        {
+            *port = MBTK_HTTPS_PORT_DEF;
+        }
+        else
+        {
+            *port = MBTK_HTTP_PORT_DEF;
+        }
+        memcpy(uri,"/",1);
+
+        LOGI("HTTP parse success:'%s','%s', %d, %d",(char*)host,(char*)uri,*port,*is_ssl);
+        return 0;
+    }
+    else
+    {
+        //LOGI("Host end with:%x",*url_ptr);
+        if(*url_ptr == ':')   // Port exist.
+        {
+            *port = atoi(url_ptr + 1);
+
+            // Point to '/' or NULL
+            while(*url_ptr && *url_ptr != '/')
+            {
+                url_ptr++;
+            }
+
+            // "www.baidu.com:80"
+            if(*url_ptr == '\0')   // No uri
+            {
+                if(*port == 0)
+                {
+                    if(*is_ssl)
+                    {
+                        *port = MBTK_HTTPS_PORT_DEF;
+                    }
+                    else
+                    {
+                        *port = MBTK_HTTP_PORT_DEF;
+                    }
+                }
+                memcpy(uri,"/",1);
+
+                LOGI("HTTP parse success:'%s','%s', %d, %d",(char*)host,(char*)uri,*port,*is_ssl);
+                return 0;
+            }
+        }
+
+        // "www.baidu.com/xxx" or "www.baidu.com:80/xxx"
+        // Now,url_ptr point to '/'
+        if(*url_ptr != '/')
+        {
+            LOGE("URI must start with '/'.");
+            return -1;
+        }
+
+        //LOGI("URL3[%d]:%s",strlen(url_ptr),url_ptr);
+
+        memcpy(uri,url_ptr,strlen(url_ptr));
+
+        if(*port == 0)
+        {
+            if(*is_ssl)
+            {
+                *port = MBTK_HTTPS_PORT_DEF;
+            }
+            else
+            {
+                *port = MBTK_HTTP_PORT_DEF;
+            }
+        }
+
+        LOGI("HTTP parse success:'%s','%s', %d, %d",(char*)host,(char*)uri,*port,*is_ssl);
+        return 0;
+    }
+}
+
+static int http_session_req_head_add(mbtk_http_session_t *session,bool replace,
+                                     char *name, char *value)
+{
+    if(session == NULL || value == NULL)
+        return -1;
+
+    LOGI("Add request header - %s:%s",name,value);
+
+    int i = 0;
+    while(i < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->req.req_h[i]
+           && !strncasecmp(session->req.req_h[i]->name,name,strlen(name)))   // Is change value
+        {
+            break;
+        }
+        i++;
+    }
+
+    if(i == HTTP_REQUEST_HEADER_MAX)   // Should add new header.
+    {
+        i = 0;
+        while(i < HTTP_REQUEST_HEADER_MAX)
+        {
+            if(session->req.req_h[i] == NULL)   // Find NULL request.
+            {
+                session->req.req_h[i] = (mbtk_http_header_t*)malloc(sizeof(mbtk_http_header_t));
+                if(session->req.req_h[i] == NULL)
+                {
+                    LOGE("malloc() fail.");
+                    return -1;
+                }
+
+                memset(session->req.req_h[i],0x0,sizeof(mbtk_http_header_t));
+                memcpy(session->req.req_h[i]->name, name, strlen(name));
+                session->req.req_h[i]->value = NULL;
+                session->req.header_cnt++;
+                break;
+            }
+            i++;
+        }
+    }
+    else     // Is change value
+    {
+        if(!replace)
+        {
+            LOGW("Found this header[%s],no replace.",name);
+            return 0;
+        }
+    }
+
+    if(i == HTTP_REQUEST_HEADER_MAX)
+    {
+        LOGE("Request header is full.");
+        return -1;
+    }
+
+    if(session->req.req_h[i]->value)
+    {
+        free(session->req.req_h[i]->value);
+    }
+    session->req.req_h[i]->value = (char*)malloc(strlen(value) + 1);
+    if(session->req.req_h[i]->value == NULL)
+    {
+        LOGE("malloc() fail.");
+        return -1;
+    }
+    memset(session->req.req_h[i]->value,0x0,strlen(value) + 1);
+    memcpy(session->req.req_h[i]->value,value,strlen(value));
+
+    return 0;
+}
+
+#if 0
+static int http_session_rsp_head_add(mbtk_http_session_t *session,
+                                     char *name, char *value)
+{
+    if(session == NULL || value == NULL)
+        return -1;
+
+    int i = 0;
+    while(i < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->rsp.rsp_h[i] == NULL)   // Find NULL request.
+        {
+            session->rsp.rsp_h[i] = (mbtk_http_header_t*)malloc(sizeof(mbtk_http_header_t));
+            if(session->rsp.rsp_h[i] == NULL)
+            {
+                LOGE("malloc() fail.");
+                return -1;
+            }
+
+            memcpy(session->rsp.rsp_h[i]->name,name,strlen(name));
+            session->rsp.rsp_h[i]->value = (char*)malloc(strlen(value) + 1);
+            if(session->rsp.rsp_h[i]->value == NULL)
+            {
+                LOGE("malloc() fail.");
+                return -1;
+            }
+            memset(session->rsp.rsp_h[i]->value,0x0,strlen(value) + 1);
+            memcpy(session->rsp.rsp_h[i]->value,value,strlen(value));
+
+            session->rsp.header_cnt++;
+            return 0;
+        }
+        i++;
+    }
+
+    return -1;
+}
+#endif
+
+static char* http_option_str_get(mbtk_http_option_enum option)
+{
+    switch(option)
+    {
+        case HTTP_OPTION_HEAD:
+            return "HEAD";
+        case HTTP_OPTION_GET:
+            return "GET";
+        case HTTP_OPTION_POST:
+            return "POST";
+        case HTTP_OPTION_PUT:
+            return "PUT";
+        case HTTP_OPTION_DELETE:
+            return "DELETE";
+        case HTTP_OPTION_OPTIONS:
+            return "OPTIONS";
+        case HTTP_OPTION_TRACE:
+            return "TRACE";
+        case HTTP_OPTION_CONNECT:
+            return "CONNECT";
+        case HTTP_OPTION_LINK:
+            return "LINK";
+        case HTTP_OPTION_UNLINK:
+            return "UNLINK";
+        default:
+            return "";
+    }
+}
+
+static char* http_version_str_get(mbtk_http_version_enum version)
+{
+    switch(version)
+    {
+        case HTTP_VERSION_1_0:
+            return "1.0";
+        case HTTP_VERSION_1_1:
+            return "1.1";
+        case HTTP_VERSION_2:
+            return "2";
+        case HTTP_VERSION_3:
+            return "3";
+        default:
+            return "";
+    }
+}
+
+static char* http_header_find(mbtk_http_session_t *session,char* name)
+{
+    int i = 0;
+    while(i < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->req.req_h[i] &&
+           !strncasecmp(session->req.req_h[i]->name,name,strlen(name)))
+        {
+            return session->req.req_h[i]->value;
+        }
+
+        i++;
+    }
+    return NULL;
+}
+
+static int http_header_str_get(mbtk_http_header_t *header,char *header_str,int header_str_len)
+{
+    if(header == NULL || header->value == NULL
+       || header_str == NULL)
+        return 0;
+
+    int len = 0;
+    len = snprintf(header_str,header_str_len,"%s: %s\r\n",
+                   header->name, header->value);
+
+    return len;
+}
+
+static mbtk_http_version_enum http_version_get_by_str(char *version_str)
+{
+    if(!memcmp(version_str,"1.0",3))
+        return HTTP_VERSION_1_0;
+    else if(!memcmp(version_str,"1.1",3))
+        return HTTP_VERSION_1_1;
+    else if(!memcmp(version_str,"2",1))
+        return HTTP_VERSION_2;
+    else if(!memcmp(version_str,"3",1))
+        return HTTP_VERSION_3;
+    else
+        return HTTP_VERSION_1_1;
+}
+
+static int http_header_read(mbtk_http_session_t *session)
+{
+#define BUFFER_SIZE 2048
+    char line[BUFFER_SIZE];
+    char *ptr = NULL;
+    int len = 0;
+    while((len = mbtk_http_read_line(session->sock_fd,line,BUFFER_SIZE)) > 0)
+    {
+        if(!memcmp(line,"\r\n",2))
+        {
+            LOGD("Read empty line.");
+            break;
+        }
+
+        // Delete "\r\n"
+        ptr = line + len - 1; // Point to last char.
+        while(http_is_space_char(*ptr))
+        {
+            *ptr = '\0';
+            len--;
+            ptr--;
+        }
+
+        LOGV("LINE:%s",line);
+
+        if(http_handles[session->handle_id].show_rsp_header &&
+           http_handles[session->handle_id].data_cb)
+        {
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_HEADER,line,len);
+        }
+
+        if(!memcmp(line,"HTTP/",5))   // "HTTP/1.1 200 OK"
+        {
+            session->rsp.state_code = atoi(line + 9);
+            session->rsp.version = http_version_get_by_str(line + 5);
+        }
+        else     // Is response header item.
+        {
+            if(!strncasecmp(line,"Content-Length",14))
+            {
+                ptr = line + 14;
+                while(ptr && !isdigit(*ptr))
+                {
+                    ptr++;
+                }
+
+                if(ptr)
+                {
+                    session->rsp.content_length = atol(ptr);
+                }
+            }
+            else if(!strncasecmp(line,"Transfer-Encoding",17))
+            {
+                ptr = line + 17;
+                while(ptr && !isalpha(*ptr))
+                {
+                    ptr++;
+                }
+
+                if(ptr && !memcmp(ptr,"chunked",7))
+                {
+                    session->rsp.is_chunked = TRUE;
+                }
+            }
+        }
+    }
+#undef BUFFER_SIZE
+
+    LOGD("RSP:HTTP/%s %d, is_chunked:%d,Content-Length:%d",http_version_str_get(session->rsp.version),
+         session->rsp.state_code,session->rsp.is_chunked,session->rsp.content_length);
+
+    return 0;
+}
+
+static int http_session_start_write(mbtk_http_session_t *session)
+{
+    LOGI("Start HTTP write.");
+
+#define BUFFER_SIZE 1024
+    session->state = HTTP_SESSION_STATE_WRITE_HEADER;
+    char buff[BUFFER_SIZE];
+    int len = 0;
+    int index = 0;
+    len += snprintf(buff + len,BUFFER_SIZE - len,"%s %s HTTP/%s\r\n",
+                    http_option_str_get(session->option),
+                    session->uri,
+                    http_version_str_get(session->version));
+
+    // if no set "Host",should set default host.
+    char *host = http_header_find(session,"Host");
+    if(!host)
+    {
+        len += snprintf(buff + len,BUFFER_SIZE - len,"Host: %s\r\n", session->host);
+    }
+
+    if(mbtk_http_write(session->sock_fd,buff,len) != len)
+    {
+        LOGE("mbtk_http_write() fail.");
+        return -1;
+    }
+
+    char header_str[BUFFER_SIZE];
+    int header_str_len = 0;
+    while(index < HTTP_REQUEST_HEADER_MAX)
+    {
+        if(session->req.req_h[index] &&
+           (header_str_len = http_header_str_get(session->req.req_h[index], header_str, BUFFER_SIZE)) > 0)
+        {
+            if(mbtk_http_write(session->sock_fd,header_str,header_str_len) != header_str_len)
+            {
+                LOGE("mbtk_http_write() fail.");
+                return -1;
+            }
+        }
+        index++;
+    }
+
+    // Write request header success.
+    LOGI("HTTP write header complete.");
+
+#undef BUFFER_SIZE
+
+    // Write "\r\n"
+    if(mbtk_http_write(session->sock_fd,"\r\n",2) != 2)
+    {
+        LOGE("mbtk_http_write() fail.");
+        return -1;
+    }
+
+    LOGI("Start write HTTPsession->option. %d", session->option);
+    if(session->option == HTTP_OPTION_POST)
+    {
+        session->state = HTTP_SESSION_STATE_WRITE_CONTENT;
+        LOGI("Start write HTTP content data.");
+
+        if(session->req.content && session->req.content_len > 0)
+        {
+            if(mbtk_http_write(session->sock_fd,session->req.content,session->req.content_len) != session->req.content_len)
+            {
+                LOGE("mbtk_http_write() fail.");
+                return -1;
+            }
+
+            session->state = HTTP_SESSION_STATE_WRITE_END;
+        }
+    }
+    else
+    {
+        session->state = HTTP_SESSION_STATE_WRITE_END;
+
+        LOGI("HTTP write complete.");
+    }
+    return 0;
+}
+
+static int http_session_read_by_chunk(mbtk_http_session_t *session)
+{
+#undef BUFFER_SIZE
+#define BUFFER_SIZE 2048
+    http_chunk_code chunk_code;
+    http_chunker_t chunker;
+    char read_buf[BUFFER_SIZE + 1];
+    int read_len = 0;
+    char chunk_buf[BUFFER_SIZE + 1];
+    int chunk_len;
+    http_chunk_init(&chunker);
+    while(TRUE)
+    {
+        read_len = mbtk_http_read(session->sock_fd,read_buf,BUFFER_SIZE,3000);
+        //read_len = mbtk_http_read_line(session->sock_file,read_buf,BUFFER_SIZE);
+        if(read_len <= 0)
+        {
+            LOGE("Read fail.");
+            return -1;
+        }
+
+        chunk_code = http_chunk_parse(&chunker, read_buf, read_len, chunk_buf, &chunk_len);
+        if(chunk_code > CHUNKE_OK)   // Fail.
+        {
+            LOGE("http_chunk_parse() fail[err - %d].",chunk_code);
+            return -1;
+        }
+
+        LOGD("Read chunk_len:%d",chunk_len);
+        chunk_buf[chunk_len] = '\0';
+
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_CONTENT,chunk_buf,chunk_len);
+
+        if(CHUNKE_STOP == chunk_code)
+        {
+            if(http_handles[session->handle_id].data_cb)
+                http_handles[session->handle_id].data_cb(session->id,
+                        MBTK_HTTP_DATA_COMPLETE,NULL,0);
+
+            break;
+        }
+    }
+
+    LOGV("Chunk read success.");
+
+    return 0;
+}
+
+static int http_session_read_by_length(mbtk_http_session_t *session)
+{
+#undef BUFFER_SIZE
+#define BUFFER_SIZE 2048
+    char read_buf[BUFFER_SIZE + 1];
+    int read_len = 0;
+    int64 read_count = 0;
+    while(TRUE)
+    {
+        memset(read_buf,0x0,BUFFER_SIZE + 1);
+        read_len = mbtk_http_read(session->sock_fd,read_buf,BUFFER_SIZE,3000);
+        if(read_len <= 0)
+        {
+            LOGE("Read fail.");
+            return -1;
+        }
+
+        if(read_count + read_len >= session->rsp.content_length)   // Read data complete.
+        {
+            if(http_handles[session->handle_id].data_cb)
+            {
+                http_handles[session->handle_id].data_cb(session->id,
+                        MBTK_HTTP_DATA_CONTENT,read_buf,session->rsp.content_length - read_count);
+
+                http_handles[session->handle_id].data_cb(session->id,
+                        MBTK_HTTP_DATA_COMPLETE,NULL,0);
+            }
+            break;
+        }
+
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_CONTENT,read_buf,read_len);
+
+        read_count += read_len;
+    }
+
+    return 0;
+}
+
+static int http_session_read_by_general(mbtk_http_session_t *session)
+{
+#undef BUFFER_SIZE
+#define BUFFER_SIZE 2048
+    char read_buf[BUFFER_SIZE + 1];
+    int read_len = 0;
+    while(TRUE)
+    {
+        read_len = mbtk_http_read(session->sock_fd,read_buf,BUFFER_SIZE,1000);
+        if(read_len <= 0)
+        {
+            if(read_len == -2 || read_len == 0) // Timeout or end
+                break;
+
+            LOGW("Read end[read_len - %d].",read_len);
+            //return -1;
+            break;
+        }
+
+        read_buf[read_len] = '\0';
+
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_CONTENT,read_buf,read_len);
+    }
+
+    if(http_handles[session->handle_id].data_cb)
+        http_handles[session->handle_id].data_cb(session->id,
+                MBTK_HTTP_DATA_COMPLETE,NULL,0);
+
+    return 0;
+}
+
+static int http_session_start_read(mbtk_http_session_t *session)
+{
+    LOGI("Start HTTP read.");
+    int result = 0;
+//    usleep(500000);
+    session->state = HTTP_SESSION_STATE_READ_HEADER;
+    if(http_header_read(session))
+    {
+        result = -1;
+        goto read_end;
+    }
+
+    if(session->option != HTTP_OPTION_HEAD)
+    {
+        session->state = HTTP_SESSION_STATE_READ_CONTENT;
+        if(session->rsp.is_chunked)
+        {
+            if(http_session_read_by_chunk(session))
+            {
+                result = -1;
+                goto read_end;
+            }
+        }
+        else if(session->rsp.content_length > 0)
+        {
+            if(http_session_read_by_length(session))
+            {
+                result = -1;
+                goto read_end;
+            }
+        }
+        else
+        {
+            if(http_session_read_by_general(session))
+            {
+                result = -1;
+                goto read_end;
+            }
+        }
+    }
+    else
+    {
+        if(http_handles[session->handle_id].data_cb)
+            http_handles[session->handle_id].data_cb(session->id,
+                    MBTK_HTTP_DATA_COMPLETE,NULL,0);
+    }
+
+read_end:
+    session->state = HTTP_SESSION_STATE_READ_END;
+
+    LOGI("HTTP request complete[result - %d].",result);
+    if(http_session_close(session))
+    {
+        return -1;
+    }
+
+#if 0
+    // Free session after HTTP request complete.
+    http_session_free(session);
+    http_handles[handle_id].session[session_id] = NULL;
+    http_handles[handle_id].session_cnt--;
+#endif
+
+    return result;
+}
+
+static bool http_session_req_check(mbtk_http_session_t *session)
+{
+    if(session == NULL || session->port == 0 ||
+       strlen(session->host) == 0)
+    {
+        LOGE("Session not set host or port.");
+        return FALSE;
+    }
+
+    if(session->option != HTTP_OPTION_HEAD &&
+       session->option != HTTP_OPTION_POST &&
+       session->option != HTTP_OPTION_GET)
+    {
+        LOGE("Only support HEAD/GET/POST");
+        return FALSE;
+    }
+
+#if 0
+    if(session->version != HTTP_VERSION_1_0 &&
+       session->version != HTTP_VERSION_1_1)
+    {
+        LOGE("Only support HTTP 1.0/1.1");
+        return FALSE;
+    }
+#endif
+
+    if(session->option == HTTP_OPTION_POST)
+    {
+        char *value = NULL;
+        value = http_header_find(session, "Content-Length");
+        if(!value)
+        {
+            LOGE("POST must set 'Content-Length'");
+            return FALSE;
+        }
+        if(session->req.content_len != atoi(value))
+        {
+            LOGE("POST 'Content-Length' error.");
+            return FALSE;
+        }
+
+        value = http_header_find(session, "Content-Type");
+        if(!value)
+        {
+            LOGE("POST must set 'Content-Type'");
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+/*************************************************************
+    Public Function Definitions
+*************************************************************/
+int mbtk_http_handle_get(bool show_rsp_header,mbtk_http_data_callback_func data_cb)
+{
+    int index = 0;
+    int i = 0;
+    for(; index < HTTP_HANDLE_MAX; index++)
+    {
+        if(http_handles[index].id < 0)   // Find free handle
+        {
+            break;
+        }
+    }
+
+    if(index == HTTP_HANDLE_MAX)
+    {
+        LOGE("HTTP Handle is full.");
+        return -1;
+    }
+
+    memset(&(http_handles[index]),0x0,sizeof(mbtk_http_handle_t));
+    http_handles[index].id = index;
+    http_handles[index].show_rsp_header = show_rsp_header;
+    http_handles[index].data_cb = data_cb;
+    http_handles[index].session_cnt = 0;
+    for(i = 0; i < HTTP_SESSION_MAX; i++)
+    {
+        http_handles[index].session[i] = NULL;
+    }
+
+    if(mbtk_http_init())
+    {
+        LOGE("mbtk_http_init() fail.");
+        return -1;
+    }
+
+    return http_handles[index].id;
+}
+
+int mbtk_http_handle_free(int handle_id)
+{
+    int i = 0;
+    if(!http_handle_check(handle_id))
+    {
+        LOGE("Handle error.");
+        return -1;
+    }
+
+    http_handles[handle_id].id = -1;
+    http_handles[handle_id].data_cb = NULL;
+    if(http_handles[handle_id].session_cnt > 0)
+    {
+        for(i = 0; i < HTTP_SESSION_MAX; i++)
+        {
+            if(http_handles[handle_id].session[i] != NULL)
+            {
+                if(http_handles[handle_id].session[i]->state != HTTP_SESSION_STATE_NON)
+                {
+                    if(http_session_close(http_handles[handle_id].session[i]))
+                    {
+                        return -1;
+                    }
+                }
+
+                http_session_free(http_handles[handle_id].session[i]);
+                http_handles[handle_id].session[i] = NULL;
+            }
+        }
+
+        http_handles[handle_id].session_cnt = 0;
+    }
+
+    if(mbtk_http_deinit())
+    {
+        LOGE("mbtk_http_deinit() fail.");
+        return -1;
+    }
+
+    return 0;
+}
+
+int mbtk_http_session_create(int handle_id, mbtk_http_option_enum option,
+                             mbtk_http_version_enum version)
+{
+    int handle_index = 0;
+    int session_index = 0;
+    if(!http_handle_check(handle_id))
+    {
+        LOGE("Handle error.");
+        return -1;
+    }
+
+    for(; handle_index < HTTP_HANDLE_MAX; handle_index++)
+    {
+        if(http_handles[handle_index].id == handle_id)   // Find handle
+        {
+            break;
+        }
+    }
+
+    if(handle_index == HTTP_HANDLE_MAX)
+    {
+        LOGE("No found handle[handle - %d].",handle_id);
+        return -1;
+    }
+
+    if(http_handles[handle_index].session_cnt >= HTTP_SESSION_MAX)
+    {
+        LOGE("Session is full.");
+        return -1;
+    }
+
+    for(; session_index < HTTP_SESSION_MAX; session_index++)
+    {
+        if(http_handles[handle_index].session[session_index] == NULL)   // Find first NULL session
+        {
+            break;
+        }
+    }
+
+    if(session_index == HTTP_SESSION_MAX)
+    {
+        LOGE("Session is full.");
+        return -1;
+    }
+
+    mbtk_http_session_t* session = (mbtk_http_session_t*)malloc(sizeof(mbtk_http_session_t));
+    if(session == NULL)
+    {
+        LOGE("malloc() fail.");
+        return -1;
+    }
+    memset(session,0x0,sizeof(mbtk_http_session_t));
+    session->sock_fd = -1;
+    session->sock_file = NULL;
+    session->handle_id = handle_id;
+    session->id = session_index;
+    session->state = HTTP_SESSION_STATE_NON;
+    session->is_ssl = FALSE;
+    session->version = version;
+    session->option = option;
+    session->req.content_len = 0;
+    session->req.content_len_send = 0;
+    session->rsp.is_chunked = FALSE;
+    session->rsp.content_length = 0;
+    session->rsp.header_cnt = 0;
+    http_handles[handle_index].session[session_index] = session;
+    http_handles[handle_index].session_cnt++;
+
+    return session->id;
+}
+
+int mbtk_http_session_option_reset(int handle_id, int session_id, mbtk_http_option_enum option)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state != HTTP_SESSION_STATE_NON)
+    {
+        LOGE("Session state error.[%d]",session->state);
+        return -1;
+    }
+
+    session->option = option;
+    return 0;
+}
+
+int mbtk_http_session_free(int handle_id,int session_id)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state != HTTP_SESSION_STATE_NON)
+    {
+        if(http_session_close(session))
+        {
+            return -1;
+        }
+    }
+
+    http_session_free(session);
+    http_handles[handle_id].session[session_id] = NULL;
+    http_handles[handle_id].session_cnt--;
+    return 0;
+}
+
+int mbtk_http_session_url_set(int handle_id,int session_id,void *url)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+        return http_url_parse(url, session->host,session->uri,&(session->port),&(session->is_ssl));
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_head_add(int handle_id,int session_id,
+                               char *name, char *value)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    if(http_str_empty(name) || http_str_empty(value))
+    {
+        LOGE("Param error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+    {
+        int result = http_session_req_head_add(session,TRUE,name,value);
+        if(!result && !strncasecmp(name,"Content-Length",14))
+        {
+            session->req.content_len = atoi(value);
+        }
+        return result;
+    }
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_content_set(int handle_id,int session_id,
+                                  char *content,uint32 content_len)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    if(content_len <= 0 || content_len > HTTP_CONTENT_LEN_MAX)
+    {
+        LOGE("Content lenght error[%d].",content_len);
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+    {
+        if(session->option != HTTP_OPTION_POST)
+        {
+            LOGE("Content only for post.");
+            return -1;
+        }
+
+        if(session->req.content)
+        {
+            free(session->req.content);
+            session->req.content_len = 0;
+        }
+
+        session->req.content = (char*)malloc(content_len);
+        if(session->req.content == NULL)
+        {
+            LOGE("malloc() fail.");
+            return -1;
+        }
+
+        char *content_type = NULL;
+        if(strlen(content) == content_len)   //
+        {
+            content_type = "text/plain";
+        }
+        else
+        {
+            content_type = "application/octet-stream";
+        }
+
+        if(http_session_req_head_add(session, FALSE, "Content-Type", content_type))
+        {
+            LOGE("Set 'Content-Type' fail.");
+            return -1;
+        }
+
+        memcpy(session->req.content,content,content_len);
+        session->req.content_len = content_len;
+
+        char len_str[20] = {0};
+        snprintf(len_str,20,"%d",content_len);
+        if(http_session_req_head_add(session,FALSE,"Content-Length",len_str))
+        {
+            LOGE("Set 'Content-Length' fail.");
+            return -1;
+        }
+
+
+        return 0;
+    }
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_start(int handle_id,int session_id)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_NON)
+    {
+        if(!http_session_req_check(session))
+        {
+            LOGE("http_session_req_check() fail.");
+            return -1;
+        }
+
+        // Must set "Connection" for post.
+        if(session->option == HTTP_OPTION_POST)
+        {
+            if(http_session_req_head_add(session,FALSE,"Connection","KeepAlive"))
+            {
+                LOGE("Set 'Content-Length' fail.");
+                return -1;
+            }
+        }
+
+        LOGI("HTTP request start.");
+        LOGI("host:%s, port:%d, uri:%s",session->host,session->port,session->uri);
+        LOGI("is_ssl:%d, version:%d, option:%d, content_len:%d",session->is_ssl,
+             session->version,session->option,session->req.content_len);
+
+        int sock_fd = mbtk_http_open(session->is_ssl,TRUE,session->host,session->port);
+        if(sock_fd < 0)
+        {
+            return -1;
+        }
+        session->sock_fd = sock_fd;
+//        int fd = mbtk_sock_fd_get(sock_fd);
+//        if(fd < 0) {
+//            LOGE("mbtk_sock_fd_get() fail.");
+//            return -1;
+//        }
+        // session->sock_file = fdopen(sock_fd,"r");
+        session->state = HTTP_SESSION_STATE_CONN;
+
+//        if(!session->sock_file) {
+//            LOGE("fdopen() fail.");
+//            return -1;
+//        }
+
+        LOGI("HTTP connected.");
+
+        if(http_session_start_write(session))
+        {
+            return -1;
+        }
+
+        if(session->state == HTTP_SESSION_STATE_WRITE_END)
+        {
+            if(http_session_start_read(session))
+            {
+                return -1;
+            }
+        }
+        else
+        {
+            LOGI("Waitting post content data...");
+        }
+
+        return 0;
+    }
+    else
+    {
+        LOGE("Currenr session is process[state - %d].",session->state);
+        return -1;
+    }
+}
+
+int mbtk_http_session_content_send(int handle_id,int session_id,
+                                   char *data,int data_len)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return -1;
+    }
+
+    if(data_len <= 0 || data_len > HTTP_CONTENT_LEN_MAX)
+    {
+        LOGE("Content lenght error[%d].",data_len);
+        return -1;
+    }
+
+    LOGV("Post send:%d - %s",data_len,data);
+
+    mbtk_http_session_t *session = http_handles[handle_id].session[session_id];
+    if(session->state == HTTP_SESSION_STATE_WRITE_CONTENT)
+    {
+        if(session->option != HTTP_OPTION_POST)
+        {
+            LOGE("Content only for post.");
+            return -1;
+        }
+
+        if(session->req.content || session->req.content_len <= 0)
+        {
+            LOGE("This post not spit package.");
+            return -1;
+        }
+
+        // Discard excess data.
+        if(session->req.content_len_send + data_len > session->req.content_len)
+            data_len = session->req.content_len - session->req.content_len_send;
+
+        if(data_len != mbtk_http_write(session->sock_fd,data,data_len))
+        {
+            return -1;
+        }
+
+        session->req.content_len_send += data_len;
+
+        LOGI("HTTP post data send: %d / %d",session->req.content_len_send,
+             session->req.content_len);
+
+        // Post data send complete.
+        if(session->req.content_len_send >= session->req.content_len)
+        {
+            session->state = HTTP_SESSION_STATE_WRITE_END;
+
+            LOGI("HTTP write complete.");
+            if(http_session_start_read(session))
+            {
+                return -1;
+            }
+        }
+
+        return 0;
+    }
+    else
+    {
+        LOGE("Currenr session state error[%d].",session->state);
+        return -1;
+    }
+}
+
+const mbtk_http_session_t* mbtk_http_session_get(int handle_id,int session_id)
+{
+    if(!http_session_check(handle_id,session_id))
+    {
+        LOGE("Session error.");
+        return NULL;
+    }
+
+    return http_handles[handle_id].session[session_id];
+}