CMNetwork/src/HTTPInputStream.cpp

272 lines
7.8 KiB
C++

#include<hgl/network/HTTPInputStream.h>
#include<hgl/network/TCPClient.h>
#include<hgl/log/LogInfo.h>
#include<hgl/type/Smart.h>
namespace hgl
{
namespace network
{
namespace
{
constexpr char HTTP_REQUEST_HEADER_BEGIN[]= " HTTP/1.1\r\n"
"Host: ";
constexpr uint HTTP_REQUEST_HEADER_BEGIN_SIZE=sizeof(HTTP_REQUEST_HEADER_BEGIN)-1;
constexpr char HTTP_REQUEST_HEADER_END[]= "\r\n"
"Accept: */*\r\n"
"User-Agent: Mozilla/5.0\r\n"
"Connection: Keep-Alive\r\n\r\n";
constexpr uint HTTP_REQUEST_HEADER_END_SIZE=sizeof(HTTP_REQUEST_HEADER_END)-1;
constexpr uint HTTP_HEADER_BUFFER_SIZE=HGL_SIZE_1KB;
}
HTTPInputStream::HTTPInputStream()
{
tcp=nullptr;
pos=-1;
filelength=-1;
tcp_is=nullptr;
http_header=new char[HTTP_HEADER_BUFFER_SIZE];
http_header_size=0;
response_code=0;
}
HTTPInputStream::~HTTPInputStream()
{
Close();
delete[] http_header;
}
/**
* 创建流并打开一个文件
* @param host 服务器地址 www.hyzgame.org.cn 或 127.0.0.1 之类
* @param filename 路径及文件名 /download/hgl.rar 之类
* @return 打开文件是否成功
*/
bool HTTPInputStream::Open(IPAddress *host_ip,const UTF8String &host_name,const UTF8String &filename)
{
Close();
response_code=0;
response_info.Clear();
response_list.Clear();
if(!host_ip)
RETURN_FALSE;
if(filename.IsEmpty())
RETURN_FALSE;
tcp=CreateTCPClient(host_ip);
char *host_ip_str=host_ip->CreateString();
SharedArray<char> self_clear(host_ip_str);
if(!tcp)
{
LOG_ERROR(U8_TEXT("Connect to HTTPServer failed: ")+UTF8String(host_ip_str));
RETURN_FALSE;
}
//设定为非堵塞模式
tcp->SetBlock(false);
//发送HTTP GET请求
int len=0;
len =strcpy(http_header ,HTTP_HEADER_BUFFER_SIZE ,"GET ",4);
len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,filename.c_str(), filename.Length());
len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,HTTP_REQUEST_HEADER_BEGIN, HTTP_REQUEST_HEADER_BEGIN_SIZE);
len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,host_name.c_str(), host_name.Length());
len+=strcpy(http_header+len,HTTP_HEADER_BUFFER_SIZE-len,HTTP_REQUEST_HEADER_END, HTTP_REQUEST_HEADER_END_SIZE);
OutputStream *tcp_os=tcp->GetOutputStream();
if(tcp_os->WriteFully(http_header,len)!=len)
{
LOG_ERROR(U8_TEXT("Send HTTP Get Info failed:")+UTF8String(host_ip_str));
delete tcp;
tcp=nullptr;
RETURN_FALSE;
}
*http_header=0;
tcp_is=tcp->GetInputStream();
return(true);
}
/**
* 关闭HTTP流
*/
void HTTPInputStream::Close()
{
pos=0;
filelength=-1;
SAFE_CLEAR(tcp);
*http_header=0;
http_header_size=0;
}
constexpr char HTTP_HEADER_SPLITE[]="\r\n";
constexpr uint HTTP_HEADER_SPLITE_SIZE=sizeof(HTTP_HEADER_SPLITE)-1;
void HTTPInputStream::ParseHttpResponse()
{
char *offset=strstr(http_header,http_header_size,HTTP_HEADER_SPLITE,HTTP_HEADER_SPLITE_SIZE);
if(!offset)
return;
response_info.SetString(http_header,offset-http_header);
char *first=strchr(http_header,' ');
if(!first)
return;
++first;
char *second=strchr(first,' ');
stou(first,second-first,response_code);
while(true)
{
first=offset+HTTP_HEADER_SPLITE_SIZE;
second=strchr(first,':',http_header_size-(first-http_header));
if(!second)break;
UTF8String key;
UTF8String value;
key.SetString(first,second-first);
first=second+2;
second=strstr(first,http_header_size-(first-http_header),HTTP_HEADER_SPLITE,HTTP_HEADER_SPLITE_SIZE);
if(!second)break;
value.SetString(first,second-first);
offset=second;
response_list.Add(key,value);
}
}
constexpr char HTTP_HEADER_FINISH[]="\r\n\r\n";
constexpr uint HTTP_HEADER_FINISH_SIZE=sizeof(HTTP_HEADER_FINISH)-1;
constexpr char HTTP_CONTENT_LENGTH[]="Content-Length: ";
constexpr uint HTTP_CONTENT_LENGTH_SIZE=sizeof(HTTP_CONTENT_LENGTH)-1;
int HTTPInputStream::PraseHttpHeader()
{
char *offset;
int size;
offset=strstr(http_header,http_header_size,HTTP_HEADER_FINISH,HTTP_HEADER_FINISH_SIZE);
if(!offset)
return 0;
ParseHttpResponse();
*offset=0;
size=http_header_size-(offset-http_header)-HTTP_HEADER_FINISH_SIZE;
if(response_code==200)
{
offset=strstr(http_header,http_header_size,HTTP_CONTENT_LENGTH,HTTP_CONTENT_LENGTH_SIZE);
if(offset)
{
offset+=HTTP_CONTENT_LENGTH_SIZE;
stou(offset,filelength);
}
//有些HTTP下载就是不提供文件长度
pos=size;
return(pos);
}
else
{
LOG_ERROR(U8_TEXT("HTTPServer error info: ")+UTF8String(http_header));
return(-1);
}
}
int HTTPInputStream::ReturnError()
{
const int err=GetLastSocketError();
if(err==nseWouldBlock)return(0); //不能立即完成
if(err==0)return(0);
LOG_ERROR(OSString(OS_TEXT("Socket Error: "))+GetSocketString(err));
Close();
RETURN_ERROR(-2);
}
/**
* 从HTTP流中读取数据,但实际读取出来的数据长度不固定
* @param buf 保存读出数据的缓冲区指针
* @param bufsize 缓冲区长度,最小1024
* @return >=0 实际读取出来的数据长度
* @return -1 读取失败
*/
int64 HTTPInputStream::Read(void *buf,int64 bufsize)
{
if(!tcp)
RETURN_ERROR(-1);
int readsize;
if(response_code==0) //HTTP头尚未解析完成
{
readsize=tcp_is->Read(http_header+http_header_size,HTTP_HEADER_BUFFER_SIZE-http_header_size);
if(readsize<=0)
return ReturnError();
http_header_size+=readsize;
readsize=PraseHttpHeader();
if(readsize==-1)
{
Close();
RETURN_ERROR(-3);
}
if(pos>0)
memcpy(buf,http_header+http_header_size-pos,pos);
return(pos);
}
else
{
readsize=tcp_is->Read((char *)buf,bufsize);
if(readsize<=0)
return ReturnError();
pos+=readsize;
return(readsize);
}
}
}//namespace network
}//namespace hgl