acl/samples/http_client/HttpClient.cpp
zsxxsz cc05b877a2 first commit acl to github
first commit acl to github
2013-08-18 17:42:25 +08:00

438 lines
9.8 KiB
C++

#include "StdAfx.h"
#include "lib_acl.h"
#include "lib_protocol.h"
#include ".\httpclient.h"
void CHttpReport::Init(void)
{
m_nBuf = 0;
m_pBuf = NULL;
}
CHttpReport::CHttpReport(int type)
{
Init();
m_type = type;
}
CHttpReport::CHttpReport(int type, double nTime)
{
Init();
m_type = type;
m_timeRes = nTime;
}
CHttpReport::CHttpReport(int type, const char *data, int len)
{
Init();
m_pBuf = (char*) acl_mymalloc((size_t) len + 1);
memcpy(m_pBuf, data, (size_t) len);
m_pBuf[len] = 0;
m_nBuf = len;
m_type = type;
}
CHttpReport::CHttpReport(int type, int len, int dummy)
{
Init();
m_type = type;
m_nContentLength = len;
}
CHttpReport::CHttpReport(int type, int len, double nTime, int dummy)
{
Init();
m_type = type;
m_nDownLen = len;
m_timeRes = nTime;
}
CHttpReport::~CHttpReport()
{
if (m_pBuf)
acl_myfree(m_pBuf);
}
CHttpClient::CHttpClient(void)
: m_sReqUrl(_T(""))
, m_bLocalSave(FALSE)
, m_sLocalFile(_T(""))
, m_nMsg(0)
, m_bDisplayReqHdr(TRUE)
, m_bDisplayResHdr(TRUE)
, m_bDisplayResBody(FALSE)
, m_bHttp11(TRUE)
, m_bZip(FALSE)
, m_bKeepAlive(FALSE)
, m_sHttpHdrAppend(_T(""))
, m_bUseAddr(FALSE)
, m_sServerAddr(_T(""))
, m_bForwardAuto(FALSE)
, m_nMaxTry(10)
{
}
CHttpClient::~CHttpClient(void)
{
}
void CHttpClient::OnDataCallback(HWND hWnd, int nMsg)
{
m_hWnd = hWnd;
m_nMsg = nMsg;
}
void CHttpClient::ReportMsg(CHttpReport *pReport, int nMsg)
{
::PostMessage(m_hWnd, WM_USER_DISPLAY, (WPARAM) pReport, 0);
}
void CHttpClient::ReportComplete(void)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_COMPLETE);
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportReqHdr(const char *data, int len)
{
if (m_nMsg <= 0)
return;
if (!m_bDisplayReqHdr)
return;
CHttpReport *pReport = new CHttpReport(TYPE_HDR_REQ, data, len);
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportResHdr(const char *data, int len)
{
if (m_nMsg <= 0)
return;
if (!m_bDisplayResHdr)
return;
CHttpReport *pReport = new CHttpReport(TYPE_HDR_RES, data, len);
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportResBody(const char *data, int len)
{
if (m_nMsg <= 0)
return;
if (!m_bDisplayResBody)
return;
CHttpReport *pReport = new CHttpReport(TYPE_BODY_RES, data, len);
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportResContentLength(int len)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_TOTAL_LEN, len, 0);
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportResDownLength(double nTime, int len)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_DOWN_LEN, len, nTime, 0);
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportErrorDns(const char *msg)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_ERROR_DNS, msg, (int) strlen(msg));
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportErrConnect(const char *msg)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_ERROR_CONNECT, msg, (int) strlen(msg));
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportErrorWrite(const char *msg)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_ERROR_WRITE, msg, (int) strlen(msg));
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportErrorRead(const char *msg)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_ERROR_READ, msg, (int) strlen(msg));
ReportMsg(pReport, m_nMsg);
}
void CHttpClient::ReportTime(double nTime)
{
if (m_nMsg <= 0)
return;
CHttpReport *pReport = new CHttpReport(TYPE_TIME_RES, nTime);
ReportMsg(pReport, m_nMsg);
}
int CHttpClient::GetUrl(const CString& url)
{
m_sReqUrl = url;
acl_pthread_t tid;
acl_pthread_create(&tid, NULL, DoRequestThread, this);
return 0;
}
void CHttpClient::SaveAs(const CString& fileName)
{
m_sLocalFile = fileName;
m_bLocalSave = TRUE;
}
static double stamp_sub(const struct timeval *from, const struct timeval *sub_by)
{
struct timeval res;
memcpy(&res, from, sizeof(struct timeval));
res.tv_usec -= sub_by->tv_usec;
if (res.tv_usec < 0) {
--res.tv_sec;
res.tv_usec += 1000000;
}
res.tv_sec -= sub_by->tv_sec;
return (res.tv_sec * 1000.0 + res.tv_usec/1000.0);
}
void *CHttpClient::DoRequestThread(void* arg)
{
CHttpClient *phClient = (CHttpClient*) arg;
HTTP_HDR_REQ *hdr_req = NULL;
HTTP_HDR_RES *hdr_res = NULL;
HTTP_RES *http_res = NULL;
ACL_VSTRING *buf = acl_vstring_alloc(256);
ACL_VSTREAM *server = NULL;
const char *pHost = NULL;
ACL_VSTREAM *fp = NULL;
int ret;
UINT nForward = 0;
time_t begin = 0;
struct timeval begin_tv, end_tv;
double time_cost = 0;
#undef RETURN
#define RETURN(_x_) do \
{ \
if (hdr_req) \
http_hdr_req_free(hdr_req); \
if (buf) \
acl_vstring_free(buf); \
if (hdr_res) \
http_hdr_res_free(hdr_res); \
if (http_res) { \
http_res->hdr_res = NULL; \
http_res_free(http_res); \
} \
if (server) \
acl_vstream_close(server); \
if (fp) \
acl_vstream_close(fp); \
gettimeofday(&end_tv, NULL); \
time_cost = stamp_sub(&end_tv, &begin_tv); \
if (time_cost >= 0) \
phClient->ReportTime(time_cost); \
phClient->ReportComplete(); \
return (_x_); \
} while(0)
const char *pUrl = phClient->m_sReqUrl.GetString();
hdr_req = http_hdr_req_create(pUrl, phClient->m_bPostMethod ? "POST" : "GET",
phClient->m_bHttp11 ? "HTTP/1.1" : "HTTP/1.0");
ASSERT(hdr_req);
if (phClient->m_bZip)
http_hdr_put_str(&hdr_req->hdr, "Accept-Encoding", "gzip, deflate");
if (phClient->m_bKeepAlive)
http_hdr_entry_replace(&hdr_req->hdr, "Connection", "Keep-Alive", 1);
if (phClient->m_bPostMethod)
http_hdr_put_int(&hdr_req->hdr, "Content-Length",
(int) phClient->m_sHttpBody.GetLength());
if (!phClient->m_sAccept.IsEmpty())
http_hdr_put_str(&hdr_req->hdr, "Accept", phClient->m_sAccept.GetString());
if (!phClient->m_sCtype.IsEmpty())
http_hdr_put_str(&hdr_req->hdr, "Content-Type", phClient->m_sCtype.GetString());
if (phClient->m_sHttpHdrAppend.GetLength() > 0) {
ACL_ARGV *argv;
HTTP_HDR_ENTRY *entry;
int i;
argv = acl_argv_split(phClient->m_sHttpHdrAppend.GetString(), "\r\n");
for (i = 0; i < argv->argc; i++) {
entry = http_hdr_entry_new(argv->argv[i]);
if (entry == NULL)
continue;
http_hdr_append_entry(&hdr_req->hdr, entry);
}
acl_argv_free(argv);
}
FORWARD:
http_hdr_build_request(hdr_req, buf);
pHost = http_hdr_req_host(hdr_req);
ASSERT(pHost);
phClient->ReportReqHdr(acl_vstring_str(buf), (int) ACL_VSTRING_LEN(buf));
CString serverAddr;
if (phClient->m_bUseAddr)
serverAddr.Format("%s", phClient->m_sServerAddr);
if (serverAddr.GetLength() == 0)
serverAddr.Format("%s", pHost);
if (strchr(serverAddr.GetString(), ':') == NULL)
serverAddr.AppendFormat(":80");
time(&begin);
gettimeofday(&begin_tv, NULL);
server = acl_vstream_connect(serverAddr.GetString(),
ACL_BLOCKING, 10, 10, 4096);
if (server == NULL) {
CString msg;
msg.Format("Connect server(%s) error", serverAddr.GetString());
phClient->ReportErrConnect(msg.GetString());
RETURN (NULL);
}
if (phClient->m_bLocalSave && fp == NULL) {
fp = acl_vstream_fopen(phClient->m_sLocalFile.GetString(),
O_WRONLY | O_CREAT | O_TRUNC, 0600, 4096);
if (fp == NULL) {
acl_msg_error("%s(%d): can't create file(%s)",
__FILE__, __LINE__, phClient->m_sLocalFile.GetString());
RETURN (NULL);
}
}
ret = acl_vstream_writen(server, acl_vstring_str(buf), ACL_VSTRING_LEN(buf));
if (ret == ACL_VSTREAM_EOF) {
acl_msg_error("%s(%d): write error", __FILE__, __LINE__);
RETURN (NULL);
}
if (phClient->m_bPostMethod && !phClient->m_sHttpBody.IsEmpty())
{
if (acl_vstream_writen(server, phClient->m_sHttpBody.GetString(),
phClient->m_sHttpBody.GetLength()) == ACL_VSTREAM_EOF)
{
acl_msg_error("%s(%d): write body error", __FILE__, __LINE__);
RETURN (NULL);
}
}
hdr_res = http_hdr_res_new();
if (http_hdr_res_get_sync(hdr_res, server, 10) < 0) {
acl_msg_error("%s(%d): get res hdr error", __FILE__, __FILE__, __LINE__);
RETURN (NULL);
}
if (http_hdr_res_parse(hdr_res) < 0) {
acl_msg_error("%s(%d): parse hdr_res error", __FILE__, __LINE__);
RETURN (NULL);
}
http_hdr_build(&hdr_res->hdr, buf);
phClient->ReportResHdr(acl_vstring_str(buf), (int) ACL_VSTRING_LEN(buf));
phClient->ReportResContentLength((int) hdr_res->hdr.content_length);
if (hdr_res->reply_status > 300 && hdr_res->reply_status < 400) {
const char* pLocation;
HTTP_HDR_REQ *hdr_req_tmp;
if (!phClient->m_bForwardAuto)
RETURN (NULL);
if (nForward++ >= phClient->m_nMaxTry) {
acl_msg_error("%s(%d): too many redirect, nForward(%d)",
__FILE__, __LINE__, nForward);
RETURN (NULL);
}
pLocation = http_hdr_entry_value(&hdr_res->hdr, "Location");
if (pLocation == NULL || *pLocation == 0) {
acl_msg_error("%s(%d): 302 reply with no Location", __FILE__, __LINE__);
RETURN (NULL);
}
hdr_req_tmp = http_hdr_req_rewrite(hdr_req, pLocation);
if (hdr_req_tmp == NULL)
RETURN (NULL);
http_hdr_req_free(hdr_req);
http_hdr_res_free(hdr_res);
hdr_req = hdr_req_tmp;
goto FORWARD;
}
http_res = http_res_new(hdr_res);
while (1) {
char tmp_buf[4096];
ret = (int) http_res_body_get_sync2(http_res, server,
tmp_buf, sizeof(tmp_buf) - 1);
if (ret <= 0)
break;
phClient->ReportResBody(tmp_buf, ret);
phClient->ReportResDownLength((int)(time(NULL) - begin), ret);
if (fp != NULL && acl_vstream_writen(fp, tmp_buf, ret) == ACL_VSTREAM_EOF) {
acl_msg_error("%s(%d): write to file error", __FILE__, __LINE__);
break;
}
}
RETURN (NULL);
}
void CHttpClient::DisplayReqHdr(BOOL enable)
{
m_bDisplayReqHdr = enable;
}
void CHttpClient::DisplayResHdr(BOOL enable)
{
m_bDisplayResHdr = enable;
}
void CHttpClient::DisplayResBody(BOOL enable)
{
m_bDisplayResBody = enable;
}
void CHttpClient::EnableHttp11(BOOL enable)
{
m_bHttp11 = enable;
}
void CHttpClient::EnableZip(BOOL enable)
{
m_bZip = enable;
}
void CHttpClient::EnableKeepAlive(BOOL enable)
{
m_bKeepAlive = enable;
}