acl/lib_acl_cpp/samples/check_trigger/http_thread.cpp

173 lines
3.4 KiB
C++

#include "stdafx.h"
#include "util.h"
#include "db_store.h"
#include "http_thread.h"
http_thread::http_thread(const char* domain, const char* ip, int port,
const char* url, double spent_dns)
: domain_(domain)
, ip_(ip)
, port_(port)
, url_(url)
, spent_dns_(spent_dns)
, spent_connect_(-1)
, spent_http_(-1)
, spent_total_(-1)
, length_(0)
, success_(false)
{
snprintf(addr_, sizeof(addr_), "%s:%d", ip, port);
}
http_thread::~http_thread()
{
db_store store;
// 线程退出前将结果入库
store.db_update(*this);
}
void* http_thread::run()
{
char domain[256];
unsigned short port;
if (acl::http_utils::get_addr(url_.c_str(), domain,
sizeof(domain), &port) == false)
{
logger_error("invalid url: %s", url_.c_str());
return NULL;
}
char host[256], *phost;
if (port == 80)
phost = domain;
else
{
snprintf(host, sizeof(host), "%s:%d", domain, port);
phost = host;
}
struct timeval begin0;
gettimeofday(&begin0, NULL);
struct timeval begin, end;
// 连接 HTTP 服务器并计算耗时
gettimeofday(&begin, NULL);
acl::socket_stream* conn = connect_server();
gettimeofday(&end, NULL);
spent_connect_ = util::stamp_sub(&end, &begin);
if (conn == NULL)
return NULL;
gettimeofday(&begin, NULL);
if (http_request(conn, phost) == false)
logger_error("http request failed!");
gettimeofday(&end, NULL);
struct timeval finish;
gettimeofday(&finish, NULL);
spent_http_ = util::stamp_sub(&end, &begin);
spent_total_ = spent_dns_ + util::stamp_sub(&finish, &begin0);
delete conn;
return NULL;
}
acl::socket_stream* http_thread::connect_server()
{
// 连接远程 HTTP 服务器
acl::socket_stream* conn = new acl::socket_stream();
if (conn->open(addr_, var_cfg_conn_timeout,
var_cfg_rw_timeout) == false)
{
logger_error("connect server %s error %s",
addr_, acl::last_serror());
return NULL;
}
return conn;
}
bool http_thread::http_request(acl::socket_stream* conn, const char* host)
{
acl::http_request req(conn);
acl::http_header& header = req.request_header();
// 构造 HTTP 请求头
header.set_url(url_.c_str())
.set_keep_alive(false)
.set_host(host)
.set_method(acl::HTTP_METHOD_GET)
.set_content_type("text/plain")
.add_entry("Accept-Encoding", "plain")
.accept_gzip(true);
// 发送 HTTP 请求头,同时读取响应头
if (req.request(NULL, 0) == false)
{
logger_error("send request to %s error %s",
conn->get_peer(true), acl::last_serror());
return false;
}
acl::http_client* client = req.get_client();
long long int content_length = client->body_length();
#if 1
acl::string buf;
int ret, len;
// 开始读数据体
while (true)
{
ret = req.read_body(buf, true, &len);
if (ret == 0)
break;
if (ret < 0)
{
logger_error("get body from %s error",
conn->get_peer(true));
return false;
}
length_ += len;
}
#else
// sohu 的 www.sohu.com 比较弱,虽然客户端发送了只接收 plain 数据
// 其依然会发 gzip 响应数据
char buf[8192];
int ret;
// 开始读数据体
while (true)
{
ret = req.get_body(buf, sizeof(buf) - 1);
if (ret == 0)
break;
if (ret < 0)
{
logger_error("get body from %s error",
conn->get_peer(true));
return false;
}
length_ += ret;
}
#endif
// 如果读到的数据与 HTTP 响应头中的数据不一致,则报错
if (content_length > 0 && length_ != content_length)
{
logger_error("length: %d != content_length: %lld, server: %s",
length_, content_length, conn->get_peer(true));
return false;
}
logger("ok, url: %s, host: %s, addr: %s", url_.c_str(), host, addr_);
success_ = true;
return true;
}