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(); ?}}
至此,就明白是什么调用了吧
muduo源码-HttpResponse.h
原文地址:https://www.cnblogs.com/perfy576/p/8669115.html