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();
  }
}

至此,就明白是什么调用了吧

原文地址:https://www.cnblogs.com/perfy576/p/8669115.html

时间: 2024-10-12 08:04:18

muduo源码-HttpResponse.h的相关文章

muduo源码-EpollPoller.h

0 设计 EpollPoller继承自Poller,为的是能够使用epoll和poll两种io函数.(这貌似是策略模式?记不清了) 1 源码 EpollPoller.h #ifndef MUDUO_NET_POLLER_EPOLLPOLLER_H #define MUDUO_NET_POLLER_EPOLLPOLLER_H #include <muduo/net/Poller.h> #include <vector> struct epoll_event; namespace mu

muduo源码-EventLoop.h

0 设计 EventLoop 类,主要是用来管理一个进程中的channel.计时器.以及epoll 的类. 每个类只有一个,(因为如果有两个,那么一个eventloop 在loop()的时候,另一个eventloop 得不到执行). 每一个channel或者说是每一个文件描述符都必须属于eventloop,换句话说,每一个文件描述符属于一个线程.(这是必然的,因为eventloop在代表了一个线程) eventloop 类的主要工作,就是wait epoll,然后执行channel 的事件处理函

muduo源码-HttpRequest.h

1 设计 HttpRequest 类,是解析完毕的请求体所存放的地方,被关联在 HttpConnext中. 2 源码 #ifndef MUDUO_NET_HTTP_HTTPREQUEST_H #define MUDUO_NET_HTTP_HTTPREQUEST_H #include <muduo/base/copyable.h> #include <muduo/base/Timestamp.h> #include <muduo/base/Types.h> #includ

muduo源码分析--我对muduo的理解

分为几个模块 EventLoop.TcpServer.Acceptor.TcpConnection.Channel等 对于EventLoop来说: 他只关注里面的主驱动力,EventLoop中只关注poll,这类系统调用使得其成为Reactor模式,EventLoop中有属于这个loop的所有Channel,这个loop属于哪一个Server. 几个类存在的意义: 从应用层使用的角度来看,用户需要初始化一个EventLoop,然后初始化一个TcpServer(当然也可以自定义个TcpServer

muduo源码分析--Reactor模式在muduo中的使用

一. Reactor模式简介 Reactor释义"反应堆",是一种事件驱动机制.和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为"回调函数". 二. moduo库Reactor模式的实现 muduo主要通过3个类来实现Reactor模式:EventLoop,Chann

muduo源码分析--Reactor模式的在muduo中的使用(二)

一. TcpServer类: 管理所有的TCP客户连接,TcpServer供用户直接使用,生命期由用户直接控制.用户只需设置好相应的回调函数(如消息处理messageCallback)然后TcpServer::start()即可. 主要数据成员: boost::scoped_ptr<Accepter> acceptor_; 用来接受连接 std::map<string,TcpConnectionPtr> connections_; 用来存储所有连接 connectonCallbac

SGI STL源码stl_bvector.h分析

前言 上篇文章讲了 STL vector 泛化版本的实现,其采用普通指针作为迭代器,可以接受任何类型的元素.但如果用来存储 bool 类型的数据,可以实现功能,但每一个 bool 占一个字节(byte),而一个字节有 8 位(bit),这样就有点浪费了.所以 SGI STL 设计了一个特化版本的位向量容器 bit_vector 来节省空间内存.bit_vector 是一个 bit 位元素的序列容器,具有 vector 容器一样的成员函数,常用于硬件端口的控制. 原文地址:https://www.

(素材源码)猫猫学IOS(二十六)UI之iOS抽屉效果小Demo

猫猫分享,必须精品 素材代码地址:http://download.csdn.net/detail/u013357243/8635679 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents 先看效果 源码 NYDrawViewController.h // // NYDrawViewController.h // 06-抽屉效果 // // Created by apple on 14-9-1. /

h5微信房卡牛牛源码学习讲解

h5微信房卡牛牛源码Q 2171793408 官网地址: http://wowotouba.com/h5 比较仔细的学习了<c++primer>,并对每个习题都自己写代码实现了一遍,包括稍微复杂一点的例子. 认真读完了<effective c++>,<effective stl>. 比较仔细的学完了<数据结构与算法分析>,并把其中的每种数据结构和算法都用c++实现了一遍.包括各种线性表,树(二叉树.AVL树.RB树的各种操作),图(BFS.DFS.prim.