HttpConnection模块概述
class HttpConnection
继承自class SocketStream
,发送请求报文,接收响应报文。
封装struct HttpResult
HTTP响应结果
实现连接池class HttpConnectionPool
,仅在长连接有效时使用。
封装class URI
,使用状态机解析URI
。
URI详解
URI
格式
foo://user@example.com:8042/over/there?name=ferret#nose
_/ ___________________/_________/ _________/ __/
| | | | |
scheme authority path query fragment
| __________________________|__
/ \ / \
urn:example:animal:ferret:nose
authority = [ userinfo "@" ] host [ ":" port ]
class Uri
再有限状态机action
中设置保存解析出来的字段。
mumber(成员变量)
// schema
std::string m_scheme;
// 用户信息
std::string m_userinfo;
// host
std::string m_host;
// 路径
std::string m_path;
// 查询参数
std::string m_query;
// fragment
std::string m_fragment;
// 端口
uint16_t m_port;
Uri(构造函数)
Uri::Uri()
:m_port(0) {
}
Create(创建URI)
静态方法
Uri::ptr Uri::Create(const std::string& uristr) {
Uri::ptr uri(new Uri);
int cs = 0;
const char* mark = 0;
// 有限状态机初始化
%% write init;
// URI字符串的起始位置
const char* p = uristr.c_str();
// 结束位置
const char* pe = p + uristr.size();
const char* eof = pe;
// 执行有限状态机
%% write exec;
if(cs == uri_parser_error) {
return nullptr;
// 达到最终状态或更高
} else if(cs >= uri_parser_first_final) {
return uri;
}
return nullptr;
}
isDefaultPort(是否为默认端口:80,443)
bool Uri::isDefaultPort() const {
if(m_port == 0) {
return true;
}
if(m_scheme == "http") {
return m_port == 80;
} else if(m_scheme == "https") {
return m_port == 443;
}
return false;
}
getPort(返回端口号)
uint16_t Uri::getPort() const {
if(m_port) {
return m_port;
}
if(m_scheme == "http") {
return 80;
} else if(m_scheme == "https"){
return 443;
}
return m_port;
}
getPath(返回路径)
const std::string& Uri::getPath() const {
static std::string s_default_path = "/";
return m_path.empty() ? s_default_path : m_path;
}
createAddress(创建地址)
Address::ptr Uri::createAddress() const {
auto addr = sylar::Address::LookupAnyIPAddress(m_host);
if(addr) {
addr->setPort(getPort());
}
return addr;
}
HttpConnection详解
HttpResult
HTTP响应结果
struct HttpResult {
typedef std::shared_ptr<HttpResult> ptr;
enum class Error {
/// 正常
OK = 0,
/// 非法URL
INVALID_URL = 1,
/// 无法解析HOST
INVALID_HOST = 2,
/// 连接失败
CONNECT_FAIL = 3,
/// 连接被对端关闭
SEND_CLOSE_BY_PEER = 4,
/// 发送请求产生Socket错误
SEND_SOCKET_ERROR = 5,
/// 超时
TIMEOUT = 6,
/// 创建Socket失败
CREATE_SOCKET_ERROR = 7,
/// 从连接池中取连接失败
POOL_GET_CONNECTION = 8,
/// 无效的连接
POOL_INVALID_CONNECTION = 9,
};
HttpResult(int _result
, HttpResponse::ptr _response
, const std::string& _error)
/// 错误码
int result;
/// HTTP响应结构体
HttpResponse::ptr response;
/// 错误描述
std::string error {
}
std::string toString() const;
// 错误码
int result;
// HTTP响应结构体
HttpResponse::ptr response;
// 错误描述
std::string error;
};
class HttpConnection
mumber(成员变量)
// 创建时间
uint64_t m_createTime = 0;
// 请求超时时间
uint64_t m_request = 0;
HttpConnection(构造函数)
HttpConnection::HttpConnection(Socket::ptr sock, bool owern)
:SocketStream(sock, owern) {
}
recvResponse(接收响应报文)
支持chunk
解析
HttpResponse::ptr HttpConnection::recvResponse() {
HttpResponseParser::ptr parser(new HttpResponseParser);
uint64_t buff_size = HttpResponseParser::GetHttpResponseBufferSize();
// 智能指针接管
std::shared_ptr<char> buffer(
new char[buff_size], [](char* ptr) {
delete[] ptr;
});
char* data = buffer.get();
int offset = 0;
do {
// 在offset后面接着读数据
int len = read(data + offset, buff_size - offset);
if (len <= 0) {
close();
return nullptr;
}
// 当前已经读取的数据长度
len += offset;
data[len] = '\0';
// 解析缓冲区data中的数据
// execute会将data向前移动nparse个字节,nparse为已经成功解析的字节数
size_t nparse = parser->execute(data, len, false);
if (parser->hasError()) {
close();
return nullptr;
}
// 此时data还剩下len - nparse个字节
offset = len - nparse;
// 缓冲区满了还没解析完
if (offset == (int)buff_size) {
close();
return nullptr;
}
// 解析结束
if (parser->isFinished()) {
break;
}
} while (true);
// 这里返回引用
auto& client_parser = parser->getParser();
// 是否为chunk
if (client_parser.chunked) {
std::string body;、
// 缓冲区剩余数据
int len = offset;
do {
do {
// 继续读数据
int rt = read(data + len, buff_size - len);
if (rt <= 0) {
close();
return nullptr;
}
// 更新缓冲区数据数量
len += rt;
// 再末尾添加
data[len] = '\0';
// 解析报文,重新初始化parser
size_t nparser = parser->execute(data, len, true);
if (parser->hasError()) {
close();
return nullptr;
}
len -= nparser;
if (len == (int)buff_size) {
close();
return nullptr;
}
} while (!parser->isFinished());
len -= 2;
// body小于缓冲区的数据
if (client_parser.content_len <= len) {
// 将body数据加进来
body.append(data, client_parser.content_len);
// 移动data
memmove(data, data + client_parser.content_len, len - client_parser.content_len);
// 缓冲区剩余数据数
len -= client_parser.content_len;
} else {
// 将缓冲区全部数据加进来
body.append(data, len);
// 还剩多少body数据
int left = client_parser.content_len - len;
while (left > 0) {
// 读最多缓冲区大小的数据
int rt = read(data, left > (int)buff_size ? (int)buff_size : left);
if (rt <= 0) {
close();
return nullptr;
}
// 加到body
body.append(data, rt);
left -= rt;
}
len = 0;
}
} while (!client_parser.chunks_done);
parser->getData()->setBody(body);
} else {
// 获得body的长度
int64_t length = parser->getContentLength();
if (length > 0) {
std::string body;
body.resize(length);
int len = 0;
// 如果长度比缓冲区剩余的还大,将缓冲区全部加进来
if (length >= offset) {
mempcpy(&body[0], data, offset);
len = offset;
} else {
// 否则将取length
memcpy(&body[0], data, length);
len = length;
}
length -= offset;
// 缓冲区里的数据也不够,继续读取直到满足length
if (length > 0) {
if (readFixSize(&body[len], length) <= 0) {
close();
return nullptr;
}
}
// 设置body
parser->getData()->setBody(body);
}
}
//返回解析完的HttpResponse
return parser->getData();
}
sendRequest(发送请求报文)
int HttpConnection::sendRequest(HttpRequest::ptr req) {
std::stringstream ss;
ss << *req;
std::string data = ss.str();
return writeFixSize(data.c_str(), data.size());
}
DoRequest(请求)
最后所有的请求都是由这个方法实现的
HttpResult::ptr HttpConnection::DoRequest(HttpMethod method
, Uri::ptr uri
, uint64_t timeout_ms
, const std::map<std::string, std::string>& headers
, const std::string& body) {
// 创建http请求报文
HttpRequest::ptr req = std::make_shared<HttpRequest>();
// 设置path
req->setPath(uri->getPath());
// 设置qurey
req->setQuery(uri->getQuery());
// 设置fragment
req->setFragment(uri->getFragment());
// 设置请求方法
req->setMethod(method);
// 是否有主机号
bool has_host = false;
for (auto& i : headers) {
if (strcasecmp(i.first.c_str(), "connection") == 0) {
if (strcasecmp(i.second.c_str(), "keep-alive") == 0) {
req->setClose(false);
}
continue;
}
// 看有没有设置host
if (!has_host && strcasecmp(i.first.c_str(), "host") == 0) {
has_host = !i.second.empty();
}
// 设置header
req->setHeader(i.first, i.second);
}
//若没有host,则用uri的host
if (!has_host) {
req->setHeader("Host", uri->getHost());
}
// 设置body
req->setBody(body);
return DoRequest(req, uri, timeout_ms);
}
HttpResult::ptr HttpConnection::DoRequest(HttpRequest::ptr req
, Uri::ptr uri
, uint64_t timeout_ms) {
// 通过uri创建address
Address::ptr addr = uri->createAddress();
if (!addr) {
return std::make_shared<HttpResult>((int)HttpResult::Error::INVAILD_HOST
, nullptr, "invalid host: " + uri->getHost());
}
// 创建TCPsocket
Socket::ptr sock = Socket::CreateTCP(addr);
if (!sock) {
return std::make_shared<HttpResult>((int)HttpResult::Error::CREATE_SOCKET_ERROR
, nullptr, "create socket fail: " + addr->toString()
+ " errno=" + std::to_string(errno)
+ " errstr=" + std::string(strerror(errno)));
}
// 发起请求连接
if (!sock->connect(addr)) {
return std::make_shared<HttpResult>((int)HttpResult::Error::CONNECT_FAIL
, nullptr, "connect fail: " + addr->toString());
}
// 设置接收超时时间
sock->setRecvTimeout(timeout_ms);
// 创建httpconnection
HttpConnection::ptr conn = std::make_shared<HttpConnection>(sock);
// 发送请求报文
int rt = conn->sendRequest(req);
// 若为0,则表示远端关闭连接
if (rt == 0) {
return std::make_shared<HttpResult>((int)HttpResult::Error::SEND_CLOSE_BY_PEER
, nullptr, "send request closed by peer: " + addr->toString());
}
// 小于0,失败
if (rt < 0) {
return std::make_shared<HttpResult>((int)HttpResult::Error::SEND_SOCKET_ERROR
, nullptr, "send request socket error, errno = " + std::to_string(errno)
+ " errstr = " + std::string(strerror(errno)));
}
// 接收响应报文
auto rsp = conn->recvResponse();
if (!rsp) {
return std::make_shared<HttpResult>((int)HttpResult::Error::TIMEOUT
, nullptr, "recv response fail, addr: " + addr->toString()
+ ", timeout_ms: " + std::to_string(timeout_ms));
}
// 结果成功,返回响应报文
return std::make_shared<HttpResult>((int)HttpResult::Error::OK, rsp, "ok");
}
HttpConnectionPool
连接池,仅在长连接时有效,Connection: keep-alive
mumber(成员变量)
// 主机
std::string m_host;
std::string m_vhost;
// 端口号
uint16_t m_port;
// 连接最大数
uint32_t m_maxSize;
// 连接时长
uint32_t m_maxAliveTime;
// 请求时长
uint32_t m_maxRequest;
// 锁
MutexType m_mutex;
// HttpConnection指针链表
std::list<HttpConnection*> m_conns;
// 连接的数量
std::atomic<int32_t> m_total = { 0 };
HttpConnectionPool(构造函数)
HttpConnectionPool::HttpConnectionPool(const std::string& host
, const std::string& vhost
, uint16_t port
, uint32_t max_size
, uint32_t max_alive_time
, uint32_t max_request)
:m_host(host)
,m_vhost(vhost)
,m_port(port)
,m_maxSize(max_size)
,m_maxAliveTime(max_alive_time)
,m_maxRequest(max_request) {
}
getConnection(获得连接)
HttpConnection::ptr HttpConnectionPool::getConnection() {
uint64_t now_ts = sylar::GetCurrentMS();
// 非法的连接
std::vector<HttpConnection*> invalid_conns;
HttpConnection* ptr = nullptr;
Mutex::Lock lock(m_mutex);
// 若连接池不为空
while (!m_conns.empty()) {
// 取出第一个connection
auto conn = *m_conns.begin();
// 弹掉
m_conns.pop_front();
// 不在连接状态,放入非法vec中
if (!conn->isConnected()) {
invalid_conns.push_back(conn);
continue;
}
// 已经超过了最大连接时间,放入非法vec中
if ((conn->m_createTime + m_maxAliveTime) > now_ts) {
invalid_conns.push_back(conn);
continue;
}
// 获得当前connection
ptr = conn;
break;
}
lock.unlock();
// 将非法con删除
for (auto& i : invalid_conns) {
delete i;
}
// 更新连接总数
m_total -= invalid_conns.size();
// 若没有连接
if (!ptr) {
// 根据host创建addr
IPAddress::ptr addr = Address::LookupAnyIPAddress(m_host);
if (!addr) {
SYLAR_LOG_ERROR(g_logger) << "get addr fail: " << m_host;
return nullptr;
}
// 设置端口号
addr->setPort(m_port);
// 创建TCPsocket
Socket::ptr sock = Socket::CreateTCP(addr);
if (!sock) {
SYLAR_LOG_ERROR(g_logger) << "create sock fail: " << *addr;
return nullptr;
}
// 连接
if (!sock->connect(addr)) {
SYLAR_LOG_ERROR(g_logger) << "sock connect fail: " << *addr;
return nullptr;
}
ptr = new HttpConnection(sock);
++m_total;
}
return HttpConnection::ptr(ptr, std::bind(&HttpConnectionPool::ReleasePtr
, std::placeholders::_1, this));
}
ReleasePtr(释放connection)
void HttpConnectionPool::ReleasePtr(HttpConnection* ptr, HttpConnectionPool* pool) {
// 已经关闭了链接,超时,超过最大请求数量
if (!ptr->isConnected()
|| ((ptr->m_createTime + pool->m_maxAliveTime) >= sylar::GetCurrentMS())
|| ptr->m_request >= pool->m_maxRequest) {
delete ptr;
--pool->m_total;
return;
}
// 请求次数+1
++ptr->m_request;
MutexType::Lock lock(pool->m_mutex);
// 重新放入连接池中
pool->m_conns.push_back(ptr);
}