| /************************************************************* |
| 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 <fcntl.h> |
| #include <unistd.h> |
| #include <sys/stat.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)) |
| { |
| LOGE("http_session_read_by_chunk fail."); |
| result = -1; |
| goto read_end; |
| } |
| } |
| else if(session->rsp.content_length > 0) |
| { |
| if(http_session_read_by_length(session)) |
| { |
| LOGE("http_session_read_by_length fail."); |
| result = -1; |
| goto read_end; |
| } |
| } |
| else |
| { |
| if(http_session_read_by_general(session)) |
| { |
| LOGE("http_session_read_by_general fail."); |
| 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->file_fd = -1; |
| session->handle_id = handle_id; |
| session->id = session_index; |
| session->state = HTTP_SESSION_STATE_NON; |
| session->is_ssl = FALSE; |
| session->ingnore_cert = TRUE; |
| 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_ingnore_cert_set(int handle_id, int session_id, bool ingnore_cert) |
| { |
| 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]; |
| |
| session->ingnore_cert = ingnore_cert; |
| |
| LOGE("session->ingnore_cert:%d, ingnore_cert:%d\n", session->ingnore_cert, ingnore_cert); |
| 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,ingnore_cert:%d, version:%d, option:%d, content_len:%d",session->is_ssl, |
| session->ingnore_cert, session->version,session->option,session->req.content_len); |
| |
| int sock_fd = mbtk_http_open(session->is_ssl,session->ingnore_cert,session->host,session->port); |
| |
| if(sock_fd < 0) |
| { |
| LOGE("mbtk_http_open() fail."); |
| 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]; |
| } |
| |
| void mbtk_http_lib_info_print() |
| { |
| MBTK_SOURCE_INFO_PRINT("mbtk_http_lib"); |
| } |
| |