HttpServer服务端测试模块-详解[5-1]

来源:互联网 发布:深圳市信诚网络 编辑:程序博客网 时间:2024/05/17 21:39

主流程如下:之前章节大体介绍了下各模块,这里我们主要看看我们的应用HttpServer类是如何使用muduo库的.

可以参考:

博客[4-2]可知,当上层初始化TcpServer之后:

由于初始化TcpServer里面,new Acceptor 这里面会建立socket 并绑定本地addr,epoll中关注该socket的读事件;由于这里给Acceptor 注册TcpServer::newConnection函数,因此当监听套接字发生读事件时候执行TcpServer::newConnection函数;

当上层接着调用TcpServer::start()该函数中调用Acceptor::listen 之后

当新客户连接时候则调用TcpServer::newConnection 函数,该函数里面来一个客户端则new一个 TcpConnection 对象, 接着给该客户端注册回调函数, 即TcpServer的上层给TcpServer设置的回调函数: connectionCallback; messageCallback_; writeCompleteCallback_;


那么这些回调函数在哪呢?

即  HttpServer初始化中

server_.setConnectionCallback(
      boost::bind(&
HttpServer::onConnection, this, _1));
 server_.setMessageCallback(
      boost::bind(&
HttpServer::onMessage, this, _1, _2, _3));



HttpServer_test.cc

#include <muduo/net/http/HttpServer.h>
#include <muduo/net/http/HttpRequest.h>
#include <muduo/net/http/HttpResponse.h>
#include <muduo/net/EventLoop.h>
#include <muduo/base/Logging.h>


#include <iostream>
#include <map>


using namespace muduo;
using namespace muduo::net;


extern char favicon[555];
bool benchmark = false;


void onRequest(const HttpRequest& req, HttpResponse* resp)
{
  std::cout << "Headers " << req.methodString() << " " << req.path() << std::endl;
  if (!benchmark)
  {
    const std::map<string, string>& headers = req.headers();
    for (std::map<string, string>::const_iterator it = headers.begin();
         it != headers.end();
         ++it)
    {
      std::cout << it->first << ": " << it->second << std::endl;
    }
  }


  if (req.path() == "/")
  {
    resp->setStatusCode(HttpResponse::k200Ok);
    resp->setStatusMessage("OK");
    resp->setContentType("text/html");
    resp->addHeader("Server", "Muduo");
    string now = Timestamp::now().toFormattedString();
    resp->setBody("<html><head><title>This is title</title></head>"
        "<body><h1>Hello</h1>Now is " + now +
        "</body></html>");
  }
  else if (req.path() == "/favicon.ico")
  {
    resp->setStatusCode(HttpResponse::k200Ok);
    resp->setStatusMessage("OK");
    resp->setContentType("image/png");
    resp->setBody(string(favicon, sizeof favicon));
  }
  else if (req.path() == "/hello")
  {
    resp->setStatusCode(HttpResponse::k200Ok);
    resp->setStatusMessage("OK");
    resp->setContentType("text/plain");
    resp->addHeader("Server", "Muduo");
    resp->setBody("hello, world!\n");
  }
  else
  {
    resp->setStatusCode(HttpResponse::k404NotFound);
    resp->setStatusMessage("Not Found");
    resp->setCloseConnection(true);
  }
}

int main(int argc, char* argv[])

{
  int numThreads = 0;
  if (argc > 1)
  {
    benchmark = true;
    Logger::setLogLevel(Logger::WARN);
    numThreads = atoi(argv[1]);
  }
  EventLoop loop;
  HttpServerserver(&loop, InetAddress(8000), "dummy");
  server.setHttpCallback(onRequest);  //HttpServerserver设置回调函数
  server.setThreadNum(numThreads);
  server.start();

  loop.loop();

}


HttpServer.h

#ifndef MUDUO_NET_HTTP_HTTPSERVER_H
#define MUDUO_NET_HTTP_HTTPSERVER_H


#include <muduo/net/TcpServer.h>
#include <boost/noncopyable.hpp>


namespace muduo
{
namespace net
{


class HttpRequest;
class HttpResponse;


/// A simple embeddable HTTP server designed for report status of a program.
/// It is not a fully HTTP 1.1 compliant server, but provides minimum features
/// that can communicate with HttpClient and Web browser.
/// It is synchronous, just like Java Servlet.
class HttpServer : boost::noncopyable
{
 public:
  typedef boost::function<void (const HttpRequest&,
                                HttpResponse*)> HttpCallback;


  HttpServer(EventLoop* loop,
             const InetAddress& listenAddr,
             const string& name,
             TcpServer::Option option = TcpServer::kNoReusePort);


  ~HttpServer();  // force out-line dtor, for scoped_ptr members.


  EventLoop* getLoop() const { return server_.getLoop(); }


  /// Not thread safe, callback be registered before calling start().
  void setHttpCallback(const HttpCallback& cb)
  {
    httpCallback_ = cb;
  }


  void setThreadNum(int numThreads)
  {
    server_.setThreadNum(numThreads);
  }


  void start();


 private:
  void onConnection(const TcpConnectionPtr& conn);
  void onMessage(const TcpConnectionPtr& conn,
                 Buffer* buf,
                 Timestamp receiveTime);
  void onRequest(const TcpConnectionPtr&, const HttpRequest&);


  TcpServer server_;
  HttpCallback httpCallback_;
};


}
}


#endif  // MUDUO_NET_HTTP_HTTPSERVER_H


HttpServer.cc

#include <muduo/net/http/HttpServer.h>


#include <muduo/base/Logging.h>
#include <muduo/net/http/HttpContext.h>
#include <muduo/net/http/HttpRequest.h>
#include <muduo/net/http/HttpResponse.h>


#include <boost/bind.hpp>


using namespace muduo;
using namespace muduo::net;


namespace muduo
{
namespace net
{
namespace detail
{


// FIXME: move to HttpContext class
bool processRequestLine(const char* begin, const char* end, HttpContext*context)
{
  bool succeed = false;
  const char* start = begin;
  const char* space = std::find(start, end, ' ');
  HttpRequest& request = context->request();
  if (space != end && request.setMethod(start, space))
  {
    start = space+1;
    space = std::find(start, end, ' ');
    if (space != end)
    {
      request.setPath(start, space);
      start = space+1;
      succeed = end-start == 8 && std::equal(start,end-1, "HTTP/1.");
      if (succeed)
      {
        if (*(end-1) == '1')
        {
          request.setVersion(HttpRequest::kHttp11);
        }
        else if (*(end-1) == '0')
        {
          request.setVersion(HttpRequest::kHttp10);
        }
        else
        {
          succeed = false;
        }
      }
    }
  }
  return succeed;
}


// FIXME: move to HttpContext class
// return false if any error
bool parseRequest(Buffer* buf, HttpContext* context, Timestamp receiveTime)
{
  bool ok = true;
  bool hasMore = true;
  while (hasMore)
  {
    if (context->expectRequestLine())
    {
      const char* crlf = buf->findCRLF();
      if (crlf)
      {
        ok = processRequestLine(buf->peek(), crlf,context);
        if (ok)
        {
         context->request().setReceiveTime(receiveTime);
          buf->retrieveUntil(crlf + 2);
          context->receiveRequestLine();
        }
        else
        {
          hasMore = false;
        }
      }
      else
      {
        hasMore = false;
      }
    }
    else if (context->expectHeaders())
    {
      const char* crlf = buf->findCRLF();
      if (crlf)
      {
        const char* colon = std::find(buf->peek(), crlf,':');
        if (colon != crlf)
        {
          context->request().addHeader(buf->peek(),colon, crlf);
        }
        else
        {
          // empty line, end of header
          context->receiveHeaders();
          hasMore = !context->gotAll();
        }
        buf->retrieveUntil(crlf + 2);
      }
      else
      {
        hasMore = false;
      }
    }
    else if (context->expectBody())
    {
      // FIXME:
    }
  }
  return ok;
}


void defaultHttpCallback(const HttpRequest&, HttpResponse* resp)
{
  resp->setStatusCode(HttpResponse::k404NotFound);
  resp->setStatusMessage("Not Found");
  resp->setCloseConnection(true);
}


}
}
}


/*
初始化TcpServer, 并给TcpServer connectionCallback_注册函数HttpServer::onConnection
TcpServer messageCallback_注册函数HttpServer::onMessage

*/
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));
}


HttpServer::~HttpServer()
{
}


/*
执行TcpServer.start();
*/
void HttpServer::start()
{
  LOG_WARN << "HttpServer[" << server_.name()
    << "] starts listenning on " <<server_.hostport();
  server_.start();
}



void HttpServer::onConnection(const TcpConnectionPtr& conn)
{
  if (conn->connected())
  {
    conn->setContext(HttpContext());//当客户端已经连接上时给客户端TcpConnectionPtr设置拥有成员为HttpContext()
  }
}


 /*当客户端发来数据时候下层会调用该函数,buf中存放的是客户端的消息,解析消息将其有用内容存于客户端TcpConnectionPtr成员context 中, 接着调用onRequest响应客户端*/
void HttpServer::onMessage(const TcpConnectionPtr& conn,
                          Buffer* buf,
                          Timestamp receiveTime)
{

  HttpContext* context = boost::any_cast<HttpContext>(conn->getMutableContext());


  if (!detail::parseRequest(buf, context, receiveTime))
  {
    conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");
    conn->shutdown();
  }


  if (context->gotAll())
  {
    onRequest(conn, context->request());
    context->reset();
  }
}

/*

即调用HttpServer_test.cc文件中main函数中 server.setHttpCallback(onRequest);注册的onRequest函数去处理

*/
void HttpServer::onRequest(const TcpConnectionPtr& conn, constHttpRequest& req)
{
  const string& connection = req.getHeader("Connection");
  bool close = connection == "close" ||
    (req.getVersion() == HttpRequest::kHttp10 && connection!= "Keep-Alive");
  HttpResponse response(close);
  httpCallback_(req, &response);
  Buffer buf;
  response.appendToBuffer(&buf);
  conn->send(&buf);
  if (response.closeConnection())
  {
    conn->shutdown();
  }
}

 

HttpContext.h

#ifndef MUDUO_NET_HTTP_HTTPCONTEXT_H
#define MUDUO_NET_HTTP_HTTPCONTEXT_H


#include <muduo/base/copyable.h>


#include <muduo/net/http/HttpRequest.h>


namespace muduo
{
namespace net
{


class HttpContext : public muduo::copyable
{
 public:
  enum HttpRequestParseState
  {
    kExpectRequestLine,
    kExpectHeaders,
    kExpectBody,
    kGotAll,
  };


  HttpContext()
    : state_(kExpectRequestLine)
  {
  }


  // default copy-ctor, dtor and assignment are fine


  bool expectRequestLine() const
  { return state_ == kExpectRequestLine; }


  bool expectHeaders() const
  { return state_ == kExpectHeaders; }


  bool expectBody() const
  { return state_ == kExpectBody; }


  bool gotAll() const
  { return state_ == kGotAll; }


  void receiveRequestLine()
  { state_ = kExpectHeaders; }


  void receiveHeaders()
  { state_ = kGotAll; }  // FIXME


  void reset()
  {
    state_ = kExpectRequestLine;
    HttpRequest dummy;
    request_.swap(dummy);
  }


  const HttpRequest& request() const
  { return request_; }


  HttpRequest& request()
  { return request_; }


 private:
  HttpRequestParseState state_;
  HttpRequest request_;
};


}
}


#endif  // MUDUO_NET_HTTP_HTTPCONTEXT_H

0 0
原创粉丝点击