muduo源码-HttpResponse.h

Posted perfy576

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了muduo源码-HttpResponse.h相关的知识,希望对你有一定的参考价值。

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的主要内容,如果未能解决你的问题,请参考以下文章

(10)muduo_base库源码分析:Timestamp.cc和Timestamp.h

muduo源码分析之Acceptor

muduo源码-HttpRequest.h

Muduo网络库源码分析 EventLoop事件循环(Poller和Channel)

Muduo源码Poller类 + EpollPoller类详解

muduo源码-EpollPoller.h