文章目录
-
- P74-P79HttpConnection
- [class Uri](#class Uri)
- [struct HttpResult](#struct HttpResult)
- [class HttpConnection](#class HttpConnection)
- [class HttpConnectionPool](#class HttpConnectionPool)
P74-P79HttpConnection
最后一个基础模块的封装,同样继承于SocketStream
,实现发送请求报文,接收响应报文。此外实现了连接池、使用状态机解析URI
class Uri
Uri格式
c
foo://user@example.com:8042/over/there?name=ferret#nose
_/ ___________________/_________/ _________/ __/
| | | | |
scheme authority path query fragment
| __________________________|__
/ \ / \
urn:example:animal:ferret:nose
authority = [ userinfo "@" ] host [ ":" port ]
成员变量
c
/// 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;
/// 端口
int32_t m_port;
Create
c
Uri::ptr Uri::Create(const std::string& uristr) {
# 初始化一个uri对象
Uri::ptr uri(new Uri);
# 状态机初始状态
int cs = 0;
const char* mark = 0;
%% write init;
# 获取传入str的头节点
const char *p = uristr.c_str();
# 获取传入str的尾节点
const char *pe = p + uristr.size();
const char* eof = pe;
%% write exec;
# 如果在解析过程中出现错误,cs会变成uri_parser_error
if(cs == uri_parser_error) {
return nullptr;
# 解析成功,返回uri
} else if(cs >= uri_parser_first_final) {
return uri;
}
return nullptr;
}
struct HttpResult
c
/**
* @brief HTTP响应结果
*/
struct HttpResult {
/// 智能指针类型定义
typedef std::shared_ptr<HttpResult> ptr;
/**
* @brief 错误码定义
*/
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,
};
/**
* @brief 构造函数
* @param[in] _result 错误码
* @param[in] _response HTTP响应结构体
* @param[in] _error 错误描述
*/
HttpResult(int _result
,HttpResponse::ptr _response
,const std::string& _error)
:result(_result)
,response(_response)
,error(_error) {}
/// 错误码
int result;
/// HTTP响应结构体
HttpResponse::ptr response;
/// 错误描述
std::string error;
std::string toString() const;
};
class HttpConnection
成员变量
c
// 创建时间
uint64_t m_createTime = 0;
// 请求超时时间
uint64_t m_request = 0;
构造函数
c
HttpConnection::HttpConnection(Socket::ptr sock, bool owner)
:SocketStream(sock, owner) {
}
recvResponse
c
// 接收响应报文
HttpResponse::ptr HttpConnection::recvResponse() {
// 创建响应报文解析器
HttpResponseParser::ptr parser(new HttpResponseParser);
// 获取http请求缓冲区的大小
uint64_t buff_size = HttpRequestParser::GetHttpRequestBufferSize();
// 使用智能指针托管一个读取buffer
std::shared_ptr<char> buffer(
new char[buff_size + 1], [](char* ptr){
delete[] ptr;
});
char* data = buffer.get();
// 读取偏移量
int offset = 0;
// 循环读取buffer中的数据
do {
// 返回读取的长度
int len = read(data + offset, buff_size - offset);
// 读取发生错误,返回空指针
if(len <= 0) {
close();
return nullptr;
}
// 当前已经读取的数据长度
len += offset;
// 这里是为了防止使用的状态机解析http时,一个断言报错
data[len] = '\0';
// 解析缓冲区data中的数据
// execute会将data向前移动nparse个字节,nparse为已经成功解析的字节数
size_t nparse = parser->execute(data, len, false);
// 解析失败返回空指针
if(parser->hasError()) {
close();
return nullptr;
}
// data剩余的长度
offset = len - nparse;
// 缓冲区满了还没解析完
if(offset == (int)buff_size) {
close();
return nullptr;
}
// 解析结束
if(parser->isFinished()) {
break;
}
} while(true);
// 获取刚才的解析结果
auto& client_parser = parser->getParser();
// 消息体
std::string body;
// 是否分块传输
if(client_parser.chunked) {
// 缓冲区剩余数据
int len = offset;
do {
// 是否为新的读取操作
bool begin = true;
do {
// // 如果不是新的读取操作或者缓冲区数据已经读取完毕
if(!begin || len == 0) {
// 从数据流中读取数据到缓冲区
int rt = read(data + len, buff_size - len);
// 如果读取失败或者连接关闭,则关闭连接并返回空指针
if(rt <= 0) {
close();
return nullptr;
}
// 更新缓冲区数据长度
len += rt;
}
data[len] = '\0';
// 解析操作,和上面类似
size_t nparse = parser->execute(data, len, true);
if(parser->hasError()) {
close();
return nullptr;
}
len -= nparse;
if(len == (int)buff_size) {
close();
return nullptr;
}
begin = false;
} while(!parser->isFinished());
//len -= 2;
SYLAR_LOG_DEBUG(g_logger) << "content_len=" << client_parser.content_len;
// 如果当前块的长度小于等于缓冲区数据长度
if(client_parser.content_len + 2 <= len) {
// 将当前块的数据追加到响应体中
body.append(data, client_parser.content_len);
// 移动数据,删除已经处理过的块
memmove(data, data + client_parser.content_len + 2
, len - client_parser.content_len - 2);
// 更新缓冲区数据长度
len -= client_parser.content_len + 2;
} else {
// 如果当前块的长度大于缓冲区数据长度
// 将缓冲区数据追加到响应体中
body.append(data, len);
// 计算剩余需要读取的数据长度
int left = client_parser.content_len - len + 2;
// 继续从数据流中读取数据直到读取到足够的数据为止
while(left > 0) {
int rt = read(data, left > (int)buff_size ? (int)buff_size : left);
if(rt <= 0) {
close();
return nullptr;
}
// 将读取的数据追加到响应体中
body.append(data, rt);
// 更新剩余需要读取的数据长度
left -= rt;
}
// 删除末尾的结束符
body.resize(body.size() - 2);
// 更新缓冲区数据长度为0
len = 0;
}
} while(!client_parser.chunks_done);
} else {
// 如果未使用分块传输编码
// 获取消息体的长度
int64_t length = parser->getContentLength();
// 如果消息体长度大于0
if(length > 0) {
body.resize(length);
// 初始化响应体数据的起始位置
int len = 0;
// 如果消息体长度大于等于缓冲区数据长度
if(length >= offset) {
memcpy(&body[0], data, offset);
len = offset;
} else {
memcpy(&body[0], data, length);
len = length;
}
// 计算剩余需要读取的数据长度
length -= offset;
if(length > 0) {
// 从数据流中读取数据到响应体的剩余位置
if(readFixSize(&body[len], length) <= 0) {
close();
return nullptr;
}
}
parser->getData()->setBody(body);
}
}
return parser->getData();
}
sendRequest
c
// 发送请求报文
int HttpConnection::sendRequest(HttpRequest::ptr rsp) {
std::stringstream ss;
ss << *rsp;
std::string data = ss.str();
return writeFixSize(data.c_str(), data.size());
}
DoRequest
c
// 执行HTTP请求,接受 HTTP 方法、URL、超时时间、请求头部和请求体作为参数,并返回一个 HttpResult::ptr 类型的智能指针。
HttpResult::ptr HttpConnection::DoRequest(HttpMethod method
, const std::string& url
, uint64_t timeout_ms
, const std::map<std::string, std::string>& headers
, const std::string& body) {
// 根据传入的url创建一个uri对象
Uri::ptr uri = Uri::Create(url);
// 如果创建失败返回错误消息
if(!uri) {
return std::make_shared<HttpResult>((int)HttpResult::Error::INVALID_URL
, nullptr, "invalid url: " + url);
}
return DoRequest(method, uri, timeout_ms, headers, body);
}
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) {
// 创建一个HttpRequest对象
HttpRequest::ptr req = std::make_shared<HttpRequest>();
// 使用uri初始化创建一个HttpRequest对象
req->setPath(uri->getPath());
req->setQuery(uri->getQuery());
req->setFragment(uri->getFragment());
req->setMethod(method);
bool has_host = false;
// 处理请求头
for(auto& i : headers) {
// 如果请求头部中包含了 Connection 字段并且值为 keep-alive,则设置请求不关闭连接,并继续下一次循环。
if(strcasecmp(i.first.c_str(), "connection") == 0) {
if(strcasecmp(i.second.c_str(), "keep-alive") == 0) {
req->setClose(false);
}
continue;
}
// 如果请求头部中包含了 Host 字段并且值非空,则将 has_host 设置为 true。
if(!has_host && strcasecmp(i.first.c_str(), "host") == 0) {
has_host = !i.second.empty();
}
// 设置请求头部中除 Connection 和 Host 之外的其他字段。
req->setHeader(i.first, i.second);
}
// 如果请求头部中没有 Host 字段,则根据解析得到的 URI 设置 Host 字段。
if(!has_host) {
req->setHeader("Host", uri->getHost());
}
req->setBody(body);
return DoRequest(req, uri, timeout_ms);
}
// 最终的实现
HttpResult::ptr HttpConnection::DoRequest(HttpRequest::ptr req
, Uri::ptr uri
, uint64_t timeout_ms) {
// bool is_ssl = uri->getScheme() == "https";
// 根据解析得到的 URI 创建地址对象
Address::ptr addr = uri->createAddress();
if(!addr) {
return std::make_shared<HttpResult>((int)HttpResult::Error::INVALID_HOST
, nullptr, "invalid host: " + uri->getHost());
}
// Socket::ptr sock = is_ssl ? SSLSocket::CreateTCP(addr) : Socket::CreateTCP(addr);
// 创建TCP套接字
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);
if(rt == 0) {
return std::make_shared<HttpResult>((int)HttpResult::Error::SEND_CLOSE_BY_PEER
, nullptr, "send request closed by peer: " + addr->toString());
}
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 timeout: " + addr->toString()
+ " timeout_ms:" + std::to_string(timeout_ms));
}
return std::make_shared<HttpResult>((int)HttpResult::Error::OK, rsp, "ok");
}
class HttpConnectionPool
成员变量
c
// 主机
std::string m_host;
std::string m_vhost;
// 端口号
uint32_t m_port;
// 连接最大数
uint32_t m_maxSize;
// 最长连接时间
uint32_t m_maxAliveTime;
// 最长请求时间
uint32_t m_maxRequest;
// 是否是https
bool m_isHttps;
// 锁
MutexType m_mutex;
// HttpConnection指针链表
std::list<HttpConnection*> m_conns;
// 连接的数量
std::atomic<int32_t> m_total = {0};
构造函数
c
HttpConnectionPool::HttpConnectionPool(const std::string& host
,const std::string& vhost
,uint32_t port
,bool is_https
,uint32_t max_size
,uint32_t max_alive_time
,uint32_t max_request)
:m_host(host)
,m_vhost(vhost)
,m_port(port ? port : (is_https ? 443 : 80))
,m_maxSize(max_size)
,m_maxAliveTime(max_alive_time)
,m_maxRequest(max_request)
,m_isHttps(is_https) {
}
getConnection
c
// 获得连接
HttpConnection::ptr HttpConnectionPool::getConnection() {
// 记录当前的时间
uint64_t now_ms = sylar::GetCurrentMS();
// 存储非法连接
std::vector<HttpConnection*> invalid_conns;
// 定义一个空的连接指针
HttpConnection* ptr = nullptr;
// 加锁
MutexType::Lock lock(m_mutex);
// 如果HttpConnection指针链表不为空
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_ms) {
invalid_conns.push_back(conn);
continue;
}
// 获得当前connection
ptr = conn;
break;
}
lock.unlock();
// 删除非法连接
for(auto i : invalid_conns) {
delete i;
}
// 更新总连接数
m_total -= invalid_conns.size();
// 如果没有连接
if(!ptr) {
// 根据host创建地址
IPAddress::ptr addr = Address::LookupAnyIPAdress(m_host);
if(!addr) {
SYLAR_LOG_ERROR(g_logger) << "get addr fail: " << m_host;
return nullptr;
}
// 设置端口号
addr->setPort(m_port);
// Socket::ptr sock = m_isHttps ? SSLSocket::CreateTCP(addr) : Socket::CreateTCP(addr);
// 创建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
c
void HttpConnectionPool::ReleasePtr(HttpConnection* ptr, HttpConnectionPool* pool) {
// 请求次数+1
++ptr->m_request;
// 已经关闭了链接,超时,超过最大请求数量
if(!ptr->isConnected()
|| ((ptr->m_createTime + pool->m_maxAliveTime) >= sylar::GetCurrentMS())
|| (ptr->m_request >= pool->m_maxRequest)) {
delete ptr;
--pool->m_total;
return;
}
// 重新放入连接池中
MutexType::Lock lock(pool->m_mutex);
pool->m_conns.push_back(ptr);
}