1 设计
HttpResponse类用来,存放需要发送给客户端的数据.
这些数据是一些原始的数据.并没有合成完整的报文.
但是提供一个接口,可以填充传入的 buffer 对象,合成完整的响应报文.
2 源码
#ifndef MUDUO_NET_HTTP_HTTPRESPONSE_H
#define MUDUO_NET_HTTP_HTTPRESPONSE_H
#include <muduo/base/copyable.h>
#include <muduo/base/Types.h>
#include <map>
namespace muduo
{
namespace net
{
class Buffer;
// 这个类用来保存构造出来的响应报文
class HttpResponse : public muduo::copyable
{
public:
// 状态吗
enum HttpStatusCode
{
kUnknown,
k200Ok = 200,
k301MovedPermanently = 301,
k400BadRequest = 400,
k404NotFound = 404,
};
// close 参数表示是否需要在发送完毕以后关闭连接
explicit HttpResponse(bool close)
: statusCode_(kUnknown),
closeConnection_(close)
{
}
// 设置
void setStatusCode(HttpStatusCode code)
{ statusCode_ = code; }
void setStatusMessage(const string& message)
{ statusMessage_ = message; }
// 发送完毕是否需要关闭链接,在后期可以更改
void setCloseConnection(bool on)
{ closeConnection_ = on; }
bool closeConnection() const
{ return closeConnection_; }
// 各种设置和添加了
void setContentType(const string& contentType)
{ addHeader("Content-Type", contentType); }
void addHeader(const string& key, const string& value)
{ headers_[key] = value; }
void setBody(const string& body)
{ body_ = body; }
// 传入一个buffer ,将响应报文完整的构造出来,用来发送
void appendToBuffer(Buffer* output) const;
private:
std::map<string, string> headers_;
HttpStatusCode statusCode_;
// FIXME: add http version
string statusMessage_;
bool closeConnection_;
string body_;
};
}
}
#endif // MUDUO_NET_HTTP_HTTPRESPONSE_H
实现
#include <muduo/net/http/HttpResponse.h>
#include <muduo/net/Buffer.h>
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
// 填充的
void HttpResponse::appendToBuffer(Buffer* output) const
{
char buf[32];
// 这是http版本
snprintf(buf, sizeof buf, "HTTP/1.1 %d ", statusCode_);
output->append(buf);
output->append(statusMessage_);
output->append("\r\n");
// 开始构造响应头
if (closeConnection_)
{
output->append("Connection: close\r\n");
}
else
{
// 当是长连接的时候,需要分包,因此由这个 Content-Length 用来分包
snprintf(buf, sizeof buf, "Content-Length: %zd\r\n", body_.size());
output->append(buf);
output->append("Connection: Keep-Alive\r\n");
}
// 添加其他的字段
for (std::map<string, string>::const_iterator it = headers_.begin();
it != headers_.end();
++it)
{
output->append(it->first);
output->append(": ");
output->append(it->second);
output->append("\r\n");
}
output->append("\r\n");
output->append(body_);
}
3 使用代码
HttpResponse 只用在了这里
void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequest& req)
{
const string& connection = req.getHeader("Connection");
// 当 请求报文中设置了关闭,或是http版本是1.0 ,再或者是 1.1 版本但是没有设置 keep-alive
// 那么都需要在发送以后关闭
bool close = connection == "close" ||
(req.getVersion() == HttpRequest::kHttp10 && connection != "Keep-Alive");
// 这里生成一个 HttpResponse 对象.其中的参数 是由上面计算来的
HttpResponse response(close);
// httpCallback_ 是由 用户编写的函数,更具请求头,来生成对应的响应报文
httpCallback_(req, &response);
// 生成一个 buffer 对象,用来保存生成的响应报文
Buffer buf;
// 从响应体中构造出 可发送的响应报文.
response.appendToBuffer(&buf);
// 发送给客户端
conn->send(&buf);
// 注意这里,当上面判断应该关闭的会后,那么这里就会关闭链接.
// 还有上面的 httpCallback_ 也是可以根据请求报文,来设置是否关闭的.
if (response.closeConnection())
{
conn->shutdown();
}
}
再看 ,httpserver 中的 httpCallback_
void setHttpCallback(const HttpCallback& cb)
{
httpCallback_ = cb;
}
然后继续看,什么时候会发送:
首先:
// 处理读事件
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
int savedErrno = 0;
// 使用的是分散读,然后聚合到一起
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0)
{
// 如果读到了数据,那么开始执行回调函数
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0)
{
// 读到的数据为0,那么直接关闭连接,说明是fin
handleClose();
}
else
{
// 当-1,那么发生了错误,应该去处理错误
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
而在TcpConnection构造的时候:
// acceptor 在获取了新的链接以后执行的回调函数
// sockfd 为acceptor的返回值,peerAddr为acceptor内获得的对端链接
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
loop_->assertInLoopThread();
// 然后 因为tcpserver 有一个 eventloop 线程池,用来实际的工作.
// 所以这句话其实是一个负载均衡,去一个eventloop,以后传入 tcpconnection
EventLoop *ioLoop = threadPool_->getNextLoop();
// 这里为tcpconneciont 命名
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
// 获取本端地址
InetAddress localAddr(sockets::getLocalAddr(sockfd));
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
// 然后创建 tcpconnection
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
// tcpserver 保存了每一个链接.每一个
// 应该是为了管理吗.
connections_[connName] = conn;
// 填充毁掉函数,注意这些毁掉函数,都不是tcpserver 的.
// 是由再上一层设置给tcpserver的
conn->setConnectionCallback(connectionCallback_);
// 注意这里, 绑定了 messageCallback_
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(
boost::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
// 到这里,tcpconnection就算是创建完了.
// 然后下面就执行 tcpconnection 的connectEstablished();
ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));
}
然后继续向上:
messageCallback_是 HttpServer 传入的
HttpServer::HttpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& name,
TcpServer::Option option)
: server_(loop, listenAddr, name, option),
httpCallback_(detail::defaultHttpCallback)
{
server_.setConnectionCallback(
boost::bind(&HttpServer::onConnection, this, _1));
// 当收到消息的时候,会调用的回调函数
server_.setMessageCallback(
boost::bind(&HttpServer::onMessage, this, _1, _2, _3));
}
因此在TcpConnection 读出数据以后,实际执行的是这个:
void HttpServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
// 获取 TcpConn绑定的那个 HttpContext 对象,解析
HttpContext* context = boost::any_cast<HttpContext>(conn->getMutableContext());
if (!context->parseRequest(buf, receiveTime))
{
conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");
conn->shutdown();
}
if (context->gotAll())
{
// 然后调用 onRequest 去构造返 要发送给客户端的响应报文
onRequest(conn, context->request());
context->reset();
}
}
至此,就明白是什么调用了吧