mrpdev/mrc_network_http.c

742 lines
15 KiB
C
Raw Normal View History

2019-03-21 13:34:44 +08:00
#include "mrc_base.h"
#include "mrc_network.h"
#include "mrc_network_http.h"
#include "stdarg.h"//for va_list
typedef DWORD HBLOCKHEAP;
#ifndef MAX_HOSTNAME_LEN
#define MAX_HOSTNAME_LEN 112
#endif
#define HTTP_RESPONSECODE_OFFSET 9
static const char sHttpMethods[][5] =
{
"GET",
"HEAD",
"POST"
};
typedef struct HttpRequest_T
{
HTTPMETHOD method;
struct HttpRequest_T* next;
}HTTPREQUEST, *PHTTPREQUEST;
typedef struct HttpData
{
//http event handler
FN_SOCKEVENT fnEvent;
//available when header responsed
uint32 responsecode;
int32 contentLength;
//cache the http header
uint32 hdrsize;
char header[HTTP_HEADER_SIZE];
//http request list on this connection
PHTTPREQUEST requests_head;
PHTTPREQUEST requests_tail;
}HTTPDATA, *PHTTPDATA;
static HTTPDATA sHttpDataPool[SOCKET_MAX_COUNT];
static HBLOCKHEAP sHttpDataHeap;
HBLOCKHEAP BlockHeap_Initialize(VOID* buffer, int buffersize, int blocksize);
VOID BlockHeap_Free(HBLOCKHEAP* pHeap, VOID* pBlock);
VOID* BlockHeap_Alloc(HBLOCKHEAP* pHeap);
static int mrc_i_isNum(uint8 code);
static int mrc_i_isNumText(char * code);
static uint32 inet_addr(const char* cp);
static char* tolower(char* str);
static PCSTR host(PCSTR url, uint16* port, uint16 def);
static uint16 get_port(PCSTR url);
static char* tolower(char* str)
{
int i = 0;
for(; str[i]; i++)
{
if(str[i] >= 'A' && str[i] <= 'Z')
str[i] += 'a' - 'A';
}
return str;
}
static PCSTR host(PCSTR url, uint16* port, uint16 def)
{
static char host[MAX_HOSTNAME_LEN];
int i, j;
if(port) *port = def;
for(i = j = 0; url[i]; i++)
{
if(url[i] == ':' && url[i+1] == '/' && url[i+2] == '/')
{
i+=3;
while(url[i] && url[i]!=':' && url[i]!='/')
{
host[j++] = url[i++];
}
if(port && url[i] == ':')
{
*port = atoi(url+i+1);
}
break;
}
}
host[j] = 0;
return host;
}
static uint16 get_port(PCSTR url)
{
uint16 port = 0;
char* p = NULL;
if( NULL == url )
{
return 80;
}
p = mrc_strstr(url,"://");
if( p )
{
p += 3;
}
else
{
p = (char*)url;
}
p = mrc_strstr(p,":");
if( p )
{
port = atoi(p+1);
}
else
{
port = 80;
}
if( port == 0 )
{
port = 80;
}
return port;
}
static int mrc_i_isNum(uint8 code)
{
if ((code>47 && code<58) || (code=='.') || (code==':') )
{
return 1;
}
return 0;
}
static int mrc_i_isNumText(char * code)
{
char * tempCode = code;
while(*tempCode)
{
if( mrc_i_isNum(*tempCode) ==0 )
{
return 0;
}
tempCode++;
}
return 1;
}
static uint32 inet_addr(const char* cp)
{
uint32 ip = 0;
int i;
char ipstr[100];
mrc_memset(ipstr, 0, 100);
for(i = 0; i < 4; i++)
{
ip <<= 8;
ip |= atoi(cp);
if(i < 3 && (NULL == (cp = mrc_strchr(cp, '.'))))
return 0;
cp++;
}
return ip;
}
#ifdef HTTP_HEAD_METHOD_ENABLE
static BOOL mrc_Http_Request_Add(PHTTPDATA pHttpData, HTTPMETHOD method)
{
PHTTPREQUEST request;
//append to the request list, must not failed
if( NULL == (request = (PHTTPREQUEST)malloc(sizeof(HTTPREQUEST))))
{
return FALSE;
}
//add to the request list
request->method = method;
request->next = NULL;
if(pHttpData->requests_head == NULL)
pHttpData->requests_head = request;
else
pHttpData->requests_tail->next = request;
pHttpData->requests_tail = request;
return TRUE;
}
static void mrc_Http_Request_Clear(PHTTPDATA pHttpData)
{
PHTTPREQUEST request;
while(pHttpData->requests_head)
{
request = pHttpData->requests_head;
pHttpData->requests_head = request->next;
free(request);
}
}
static void mrc_Http_Request_Pop(PHTTPDATA pHttpData)
{
PHTTPREQUEST request = pHttpData->requests_head;
if(request)
{
pHttpData->requests_head = request->next;
if(pHttpData->requests_head == NULL)
pHttpData->requests_tail = NULL;
free(request);
}
}
#endif
#define FIND_HTTP_HEADER_ENDING(pHttpData) \
(pHttpData->hdrsize >= 4 \
&& pHttpData->header[pHttpData->hdrsize - 4] == '\r' \
&& pHttpData->header[pHttpData->hdrsize - 3] == '\n' \
&& pHttpData->header[pHttpData->hdrsize - 2] == '\r' \
&& pHttpData->header[pHttpData->hdrsize -1] == '\n')
#define RESET_HTTP_RESPONSE_INFO(pHttpData) \
pHttpData->contentLength = 0; \
pHttpData->hdrsize = 0; \
pHttpData->responsecode = 0
#ifdef HTTP_HEAD_METHOD_ENABLE
#define HANDLE_RESPONSE_END(socket, pHttpData) \
if(pHttpData->fnEvent(socket, HTTPEVT_RESPONSE_END, NULL)) \
return 1; \
RESET_HTTP_RESPONSE_INFO(pHttpData); \
Http_Request_Pop(pHttpData)
#else
#define HANDLE_RESPONSE_END(socket, pHttpData) \
if(pHttpData->fnEvent(socket, HTTPEVT_RESPONSE_END, NULL)) \
return 1; \
RESET_HTTP_RESPONSE_INFO(pHttpData)
#endif
static DWORD mrc_Http_HandleResponseData(PSOCKET socket, PSOCKEVTDATA data)
{
int32 i;
PCSTR len;
PHTTPDATA pHttpData = (PHTTPDATA)socket->userdata;
if(pHttpData->responsecode == 0)
{
//receive the http header
for(i = 0; i < data->size; i++)
{
//check the http header buffer, when this happen user should adjust the value of HTTP_HEADER_SIZE
//todo
if(pHttpData->hdrsize >= HTTP_HEADER_SIZE)
{
mrc_Http_Close(socket, HTTPEVT_ERROR);
return 1;
}
//copy the byte and continue when not found the http header ending
pHttpData->header[pHttpData->hdrsize++] = data->buffer[i];
if(!FIND_HTTP_HEADER_ENDING(pHttpData))
continue;
//find the header
pHttpData->header[pHttpData->hdrsize] = 0;
//SGL_LOG("header.txt", pHttpData->header, pHttpData->hdrsize);
tolower(pHttpData->header);
//maybe socket been closed, user do not want to deal the data left
//Note:changed by huangsunbo 2008-10-20,respond head data
//todo
if(pHttpData->fnEvent(socket, HTTPEVT_RESPONSE_HEADER, data))
return 1;
#ifdef HTTP_HEAD_METHOD_ENABLE
if(pHttpData->requests_head->method == HEAD)
{
HANDLE_RESPONSE_END(socket, pHttpData);
}else{
#endif
if(NULL == (len = mrc_Http_GetResponseField(socket, HTTP_FIELD_CONTENTLENGTH)) )
{
//can not support the response do not have the field "Content-Length"
mrc_Http_Close(socket, HTTPEVT_ERROR);
return 1;
}
pHttpData->contentLength = atoi(len);
pHttpData->responsecode = mrc_Http_GetResponseCode(socket);
if(pHttpData->contentLength == 0)
{
HANDLE_RESPONSE_END(socket, pHttpData);
}
#ifdef HTTP_HEAD_METHOD_ENABLE
}
#endif
i++; //i is the bytes has been copyed, so should add 1
break;
}
data->buffer += i;
data->size -= i;
}
if(data->size > 0)
{
int32 notifybytes = MIN(pHttpData->contentLength, data->size);
int32 remainbytes = data->size - pHttpData->contentLength;
data->size = notifybytes;
//maybe socket been closed, user do not want to deal the data left
if(pHttpData->fnEvent(socket, HTTPEVT_RESPONSE_DATA, data))
return 1;
pHttpData->contentLength -= notifybytes;
data->size -= notifybytes;
data->buffer += notifybytes;
data->size = remainbytes;
if(pHttpData->contentLength == 0)
{
HANDLE_RESPONSE_END(socket, pHttpData);
}
if(data->size > 0)
return mrc_Http_HandleResponseData(socket, data);
}
return 0;
}
static void mrc_Http_Destroy(PSOCKET socket)
{
PHTTPDATA pHttpData = (PHTTPDATA)socket->userdata;
#ifdef HTTP_HEAD_METHOD_ENABLE
Http_Request_Clear(pHttpData);
#endif
BlockHeap_Free(&sHttpDataHeap, pHttpData);
}
static DWORD mrc_Http_HandleSocketEvents(PSOCKET socket, DWORD evt, PSOCKEVTDATA data)
{
PHTTPDATA pHttpData = (PHTTPDATA)socket->userdata;
FN_SOCKEVENT fnEvent = pHttpData->fnEvent;
switch(evt)
{
case SOCKEVT_CONNECTED:
{
return fnEvent(socket, evt, data);
}
case SOCKEVT_RECVDATA:
{
/*
* HTTPEVT_RESPONSE_HEADER sent to the user after get the http response header
* HTTPEVT_RESPONSE_DATA sent to the user when receive the http data,
* HTTPEVT_RESPONSE_END sent to the user when request finished
*/
return mrc_Http_HandleResponseData(socket, data);
}
case SOCKEVT_CONNECTFAILED:
case SOCKEVT_ERROR:
case SOCKEVT_CLOSED:
{
mrc_Http_Destroy(socket);
return fnEvent(socket, evt, data);
}
}
return 0;
}
void mrc_Http_Initialize(void)
{
sHttpDataHeap = BlockHeap_Initialize(sHttpDataPool, sizeof(sHttpDataPool), sizeof(HTTPDATA));
}
void mrc_Http_Terminate(void)
{
//DO NOTHING
}
/*
֧<EFBFBD><EFBFBD>ֱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ
<EFBFBD><EFBFBD><EFBFBD>Ľ<EFBFBD> 2010-03-30
*/
PSOCKET mrc_Http_OpenEx(char*ip_string,uint16 port,FN_SOCKEVENT fnEvent)
{
PHTTPDATA pHttpData;
PSOCKET socket;
if(!ip_string)
{
return NULL;
}
if( NULL == (pHttpData = BlockHeap_Alloc(&sHttpDataHeap)))
{
return NULL;
}
pHttpData->fnEvent = fnEvent;
pHttpData->contentLength = 0;
pHttpData->responsecode = 0;
pHttpData->hdrsize = 0;
#ifdef HTTP_HEAD_METHOD_ENABLE
pHttpData->requests_head = NULL;
pHttpData->requests_tail = NULL;
#endif
if(NULL == (socket = mrc_Socket_Create(SOCKPROTO_TCP,
mrc_Http_HandleSocketEvents, (DWORD)pHttpData)))
{
BlockHeap_Free(&sHttpDataHeap, pHttpData);
return NULL;
}
if(mrc_Socket_ConnectNoProxy(socket, ip_string,port,1))
{
return socket;
}
else
{
mrc_Socket_Close(socket,SOCKEVT_CLOSED);
BlockHeap_Free(&sHttpDataHeap, pHttpData);
return NULL;
}
}
BOOL mrc_Http_Close(PSOCKET socket, DWORD evt)
{
return mrc_Socket_Close(socket, evt);
}
PCSTR mrc_Http_FormatHeader(uint32* size, HTTPMETHOD method, PCSTR url,...)
{
static char header[HTTP_HEADER_SIZE];
uint32 hdrsize;
PCSTR field, value;
va_list args;
va_start(args, url);
//format the request header line
sprintf(header, HTTP_HEADER_FORMAT, sHttpMethods[method], url);
hdrsize = strlen(header);
//parse other fields and values
while(NULL != (field = va_arg(args, PCSTR)))
{
//field name
strcpy(header + hdrsize, field);
hdrsize += strlen(field);
header[hdrsize++] = ':';
header[hdrsize++] = ' ';//add space by huangsunbo
//field value
value = va_arg(args, PCSTR);
strcpy(header + hdrsize, value);
hdrsize += strlen(value);
//field ending
header[hdrsize++] = '\r';
header[hdrsize++] = '\n';
}
//append the http header ending
header[hdrsize++] = '\r';
header[hdrsize++] = '\n';
va_end(args);
*size = hdrsize;
return header;
}
BOOL mrc_Http_Send(PSOCKET socket, HTTPMETHOD method, PBYTE buffer, uint32 size)
{
#ifdef HTTP_HEAD_METHOD_ENABLE
PHTTPDATA pHttpData = (PHTTPDATA)socket->userdata;
#endif
if(!mrc_Socket_Send(socket, buffer, size))
return FALSE;
#ifdef HTTP_HEAD_METHOD_ENABLE
//append to the request list, must not failed
if(!mrc_Http_Request_Add(pHttpData, method))
{
mrc_Http_Close(socket, HTTPEVT_ERROR);
return FALSE;
}
#endif
return TRUE;
}
BOOL mrc_Http_GetEx(PSOCKET socket, PCSTR url, uint32 from, uint32 to)
{
PCSTR pHeader, pHost;
uint32 hdrsize;
uint16 port;
uint8 host_buf[128] = {0};
pHost = host(url,NULL,80);
port = get_port(url);
//<2F><><EFBFBD><EFBFBD>ת<EFBFBD><D7AA><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD>Ͷ˿<CDB6>
/*<2A><><EFBFBD><EFBFBD>ע<EFBFBD><D7A2>*/
sprintf((char*)host_buf,"%s:%d",pHost,port);
//<1>CP<43><50><EFBFBD><EFBFBD><EFBFBD><EFBFBD>http<74><70><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֻ<EFBFBD><D6BB>Ҫͨ<D2AA><CDA8>sky<6B><79>proxy<78><79><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>
//<2F><><EFBFBD><EFBFBD>ֻ<EFBFBD><D6BB>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD>sky<6B><79>server<65><72>Ȼ<EFBFBD><C8BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ת<EFBFBD><D7AA>
//<2F><><EFBFBD><EFBFBD>Ҫ<EFBFBD>޸<EFBFBD>url
//<2><3E><><EFBFBD><EFBFBD>cmwap<61><70><EFBFBD>ŷ<EFBFBD>ʽ<EFBFBD><CABD>ʱ<EFBFBD><CAB1>,<2C><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫͨ<D2AA><CDA8>cmcc<63><63><EFBFBD><EFBFBD><EFBFBD>ؽ<EFBFBD><D8BD><EFBFBD>ת<EFBFBD><D7AA>
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ҫͨ<D2AA><CDA8>Host<73><74>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ͷ˿<CDB6><CBBF><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ؽ<EFBFBD><D8BD><EFBFBD>ת<EFBFBD><D7AA>
if(from > 0)
{
char tmp[24] = {0};
if(to > from)
sprintf(tmp, "bytes=%d-%d", from, to);
else
sprintf(tmp, "bytes=%d-", from);
pHeader = mrc_Http_FormatHeader(&hdrsize, GET, url,
HTTP_FIELD_HOST, host_buf,
HTTP_FIELD_UA, HTTP_VALUE_UA,
HTTP_FIELD_ACCEPT, HTTP_VALUE_ACCEPT,
HTTP_FIELD_CONTENTTYPE, HTTP_VALUE_CONTENTTYPE,
HTTP_FIELD_CONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
HTTP_FIELD_PROXYCONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
HTTP_FIELD_RANGE, tmp,
NULL); //do not forget the NULL ending
}
else
{
/**/
pHeader = mrc_Http_FormatHeader(&hdrsize, GET, url,
HTTP_FIELD_HOST, host_buf,
HTTP_FIELD_UA, HTTP_VALUE_UA,
HTTP_FIELD_ACCEPT, HTTP_VALUE_ACCEPT,
HTTP_FIELD_CONTENTTYPE, HTTP_VALUE_CONTENTTYPE,
HTTP_FIELD_CONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
HTTP_FIELD_PROXYCONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
NULL); //do not forget the NULL ending
}
return mrc_Http_Send(socket, GET, (PBYTE)pHeader, hdrsize);
}
#ifdef HTTP_HEAD_METHOD_ENABLE
BOOL mrc_Http_Head(PSOCKET socket, PCSTR url)
{
PCSTR pHeader, pHost;
uint32 hdrsize;
pHost = host(url, NULL, 0);
pHeader = mrc_Http_FormatHeader(&hdrsize, HEAD, url,
HTTP_FIELD_HOST, pHost,
HTTP_FIELD_UA, HTTP_VALUE_UA,
HTTP_FIELD_ACCEPT, HTTP_VALUE_ACCEPT,
HTTP_FIELD_CONTENTTYPE, HTTP_VALUE_CONTENTTYPE,
HTTP_FIELD_CONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
HTTP_FIELD_PROXYCONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
NULL); //do not forget the NULL ending
return mrc_Http_Send(socket, HEAD, (PBYTE)pHeader, hdrsize);
}
#endif
BOOL mrc_Http_PostEx(PSOCKET socket, PCSTR url, PCSTR buffer, uint32 size)
{
PCSTR pHeader, pHost;
uint32 hdrsize;
char tmp[12]={0};
uint8 host_buf[128] = {0};
uint16 port;
pHost = host(url,NULL,80);
port = get_port(url);
//<2F><><EFBFBD><EFBFBD>ת<EFBFBD><D7AA><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD>Ͷ˿<CDB6>
sprintf((char*)host_buf,"%s:%d",pHost,port);
sprintf(tmp, "%d", size);
pHeader = mrc_Http_FormatHeader(&hdrsize, POST, url,
HTTP_FIELD_HOST, host_buf,
HTTP_FIELD_UA, HTTP_VALUE_UA,
HTTP_FIELD_ACCEPT, HTTP_VALUE_ACCEPT,
HTTP_FIELD_CONTENTTYPE, HTTP_VALUE_CONTENTTYPE,
HTTP_FIELD_CONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
HTTP_FIELD_PROXYCONNECTION, HTTP_VALUE_CONNECTION_KEEPALIVE,
HTTP_FIELD_CONTENTLENGTH, tmp,
NULL); //do not forget the NULL ending
//check that socket have enough buffer
if(socket->begin + SOCKET_SENDBUFFER_SIZE - socket->end < hdrsize + size)
{
return FALSE;
}
//if return false that cause is not about send buffer
if(!mrc_Http_Send(socket, POST, (PBYTE)pHeader, hdrsize))
return FALSE;
//if return false that cause is not about send buffer
if(size > 0 && !mrc_Socket_Send(socket, (PBYTE)buffer, size))
return FALSE;
return TRUE;
}
uint32 mrc_Http_GetResponseCode(PSOCKET socket)
{
PHTTPDATA pHttpData = (PHTTPDATA) socket->userdata;
return atoi(pHttpData->header + HTTP_RESPONSECODE_OFFSET);
}
int mrc_Http_GetResponseHead(PSOCKET socket,uint8** buf, uint32* size)
{
PHTTPDATA pHttpData = NULL;
if( NULL == socket || NULL == buf || size == NULL ||
socket->sd < 0 || socket->protocol != SOCKPROTO_TCP )
{
buf = NULL;
*size = 0;
return MR_FAILED;
}
pHttpData = (PHTTPDATA) socket->userdata;
*buf = (uint8*)pHttpData->header;
*size = pHttpData->hdrsize;
return MR_SUCCESS;
}
PCSTR mrc_Http_GetResponseField(PSOCKET socket, PCSTR field)
{
static char value[HTTP_FIELDVALUE_SIZE];
int32 i = 0;
PCSTR pTmp;
PHTTPDATA pHttpData = (PHTTPDATA)socket->userdata;
strncpy(value, field, HTTP_FIELDVALUE_SIZE-1);
value[HTTP_FIELDVALUE_SIZE-1] = 0;
tolower(value);
if( NULL == (pTmp = mrc_strstr(pHttpData->header, value)))
return NULL;
pTmp += mrc_strlen(field);
while(*pTmp == ' ' || *pTmp == ':')
pTmp++;
while(!(pTmp[i] == '\r' && pTmp[i+1] == '\n'))
{
value[i] = pTmp[i];
i++;
}
value[i] = 0;
return value;
}
HBLOCKHEAP BlockHeap_Initialize(VOID* buffer, int buffersize, int blocksize)
{
PBYTE pBlock = (PBYTE)buffer;
//SGL_ASSERT(0 == MOD(buffersize, blocksize) && 0 == MOD(blocksize, 4));
for(buffersize -= blocksize; pBlock - (PBYTE)buffer < buffersize ; pBlock += blocksize)
*(Uint8**)pBlock = pBlock + blocksize;
*(PBYTE*)pBlock = NULL;
return (HBLOCKHEAP)buffer;
}
VOID* BlockHeap_Alloc(HBLOCKHEAP* pHeap)
{
VOID* pBlock = (VOID*)(*pHeap);
if(pBlock)
*pHeap =(HBLOCKHEAP)( *(PBYTE*)(*pHeap) );
return pBlock;
}
VOID BlockHeap_Free(HBLOCKHEAP* pHeap, VOID* pBlock)
{
*(PBYTE*)pBlock = (PBYTE)(*pHeap);
*pHeap = (HBLOCKHEAP)pBlock;
}