muduo源码中内嵌了一个简单的HTTP server程序,源码见muduo/net/http
。看了源码之后受益匪浅。
muduo里面的HttpServer目前只支持GET和HEAD方法,获取资源的Content-Type
在源代码中写死,本文主要就是添加了一个读取mime.type的功能。希望之后能继续扩展这个简单的HttpServer,支持CGI和POST。
(一)HttpServer
跟简单的echo server套路一样,首先定义一个HttpServer
,它需要用EventLoop对象构造,包含一个TcpServer成员等。唯一的区别就是多了一个onRequest
回调
//HttpServer.h
#ifndef __HTTPSERVER_H__
#define __HTTPSERVER_H__
#include <muduo/base/Logging.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpServer.h>
#include <boost/noncopyable.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <iostream>
#include <string>
#include <muduo/net/Buffer.h>
#include "HttpContext.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
using namespace std;
class HttpServer : boost::noncopyable
{
public:
//http回调类型
typedef boost::function<void (const HttpRequest&,
HttpResponse*)> HttpCallback;
//构造函数
HttpServer(muduo::net::EventLoop* loop,
const muduo::net::InetAddress& listenAddr);
~HttpServer(); // force out-line dtor, for scoped_ptr members.
muduo::net::EventLoop* getLoop() const { return server_.getLoop(); }
void setHttpCallback(const HttpCallback& cb)
{
httpCallback_ = cb;
}
void setThreadNum(int numThreads)
{
server_.setThreadNum(numThreads);
}
void start();
private:
//onConnection
void onConnection(const muduo::net::TcpConnectionPtr &conn);
//onMessage
void onMessage(const muduo::net::TcpConnectionPtr&conn,
muduo::net::Buffer*buf,
muduo::Timestamp receiveTime);
//+++++
void onRequest(const muduo::net::TcpConnectionPtr&,const HttpRequest&);
muduo::net::TcpServer server_;
HttpCallback httpCallback_;
};
#endif
//HttpServer.cpp
#include "HttpServer.h"
#if 1
//默认HTTP回调,返回错误码
void defaultHttpCallback(const HttpRequest&, HttpResponse* resp)
{
#if 1
resp->setStatusCode(HttpResponse::CODE_400);
resp->setStatusMessage("Not Found");
resp->setCloseConnection(true);
#endif
}
#endif
#if 1
//构造函数
HttpServer::HttpServer(muduo::net::EventLoop* loop,
const muduo::net::InetAddress& listenAddr)
: server_(loop, listenAddr, "MyHttpd"),httpCallback_(defaultHttpCallback)
{
server_.setConnectionCallback(
boost::bind(&HttpServer::onConnection, this, _1));
server_.setMessageCallback(
boost::bind(&HttpServer::onMessage, this, _1, _2, _3));
}
//析构函数
HttpServer::~HttpServer()
{
}
void HttpServer::start()
{
LOG_WARN << "HttpServer[" << server_.name()
<< "] starts listenning on " << server_.ipPort();
server_.start();
}
#endif
//新连接回调
void HttpServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
if (conn->connected())
{
conn->setContext(HttpContext());
}
}
//消息回调
void HttpServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp receiveTime)
{
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())//state_==gotALL
{
//请求已经解析完毕
onRequest(conn, context->request());
context->reset();//context reset
}
}
#if 1
void HttpServer::onRequest(const muduo::net::TcpConnectionPtr& conn, const HttpRequest& req)
{
#if 1
const string& connection = req.getHeader("Connection");
bool close = connection == "close" ||
(req.getVersion() == HttpRequest::HTTP10 && connection != "Keep-Alive");
HttpResponse response(close);//构造响应
httpCallback_(req, &response);//用户回调
muduo::net::Buffer buf;
//此时response已经构造好,将向客户发送Response添加到buffer中
response.appendToBuffer(&buf);
conn->send(&buf);
//如果非Keep-Alive则直接关掉
if (response.closeConnection())
{
conn->shutdown();
}
#endif
}
#endif
按照TcpServer接收连接->处理连接这个顺序来看,首先将调用onConnection,该函数为Tcp连接创建Http上下文变量HttpContext
,这个类主要用于接收客户请求(这里为Http请求),并解析请求。
(二)HttpContext
//HttpContext.h
#ifndef __HTTPCONTEXT_H__
#define __HTTPCONTEXT_H__
#include <muduo/base/copyable.h>
#include <muduo/net/Buffer.h>
#include "HttpRequest.h"
class Buffer;
class HttpContext:public muduo::copyable
{
public:
//解析请求的状态
enum HttpRequestParseState
{
kExpectRequestLine,
kExpectHeaders,
kExpectBody,
kGotAll,
};
HttpContext()
: state_(kExpectRequestLine)
{
}
bool parseRequest(muduo::net::Buffer* buf, muduo::Timestamp receiveTime)