本文地址:(LYanger的博客:http://blog.csdn.net/freeelinux/article/details/53643802)
首先看一下http request:
#include
#include
#include
#include
#include
#include
#include
class HttpServer : boost::noncopyable
{
public:
typedef boost::function 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);
void setThreadNum(int 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_; //http服务器也是一个Tcp服务器,所以包含一个TcpServer
HttpCallback httpCallback_; //在处理http请求时(即调用onRequest)的过程中回调此函数,对请求进行具体的处理。
};
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));
}
void HttpServer::onConnection(const TcpConnectionPtr& conn)
{
if (conn->connected())
{
//构造一个http上下文对象,用来解析http请求
conn->setContext(HttpContext()); //TcpConnection和一个HttpContext绑定,利用boost::any
}
}
class HttpContext : public muduo::copyable
{
public:
enum HttpRequestParseState //解析请求状态的枚举常量
{
kExpectRequestLine, //当前正处于解析请求行的状态
kExpectHeaders, //当前正处于解析请求头部的状态
kExpectBody, //当前正处于解析请求实体的状态
kGotAll, //解析完毕
};
HttpContext()
: state_(kExpectRequestLine) //初始状态,期望收到一个请求行
{
}
// default copy-ctor, dtor and assignment are fine
// return false if any error
bool parseRequest(Buffer* buf, Timestamp receiveTime);
bool gotAll() const
{ return state_ == kGotAll; }
//重置HttpContext状态,异常安全
void reset()
{
state_ = kExpectRequestLine;
HttpRequest dummy; //构造一个临时空HttpRequest对象,和当前的成员HttpRequest对象交换置空,然后临时对象析构
request_.swap(dummy);
}
const HttpRequest& request() const
{ return request_; }
HttpRequest& request()
{ return request_; }
private:
bool processRequestLine(const char* begin, const char* end);
HttpRequestParseState state_; //请求的解析状态
HttpRequest request_; //http请求类
};
它就是一个http的上下文对象,实际上是一个状态机。针对http包进行处理,我们可以看出它在处理过程中在四种状态间切换:
void HttpServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
//取出请求,mutable可以改变
HttpContext* context = boost::any_cast(conn->getMutableContext());
//调用context的parseRequest解析请求,返回bool是否请求成功
if (!context->parseRequest(buf, receiveTime))
{
conn->send("HTTP/1.1 400 Bad Request\r\n\r\n"); //失败,发送400
conn->shutdown(); //关闭连接
}
if (context->gotAll()) //请求成功
{
//调用onRequest
onRequest(conn, context->request());
//一旦请求处理完毕,重置context,因为HttpContext和TcpConnection绑定了,我们需要解绑重复使用。
context->reset();
}
}
bool HttpContext::parseRequest(Buffer* buf, Timestamp receiveTime)
{
bool ok = true;
bool hasMore = true;
while (hasMore)
{
//初始状态是处于解析请求行的状态,下一次循环不是该状态就不会进入,一般只进入一次
if (state_ == kExpectRequestLine)
{
//首先查找\r\n,就会到GET / HTTP/1.1的请求行末尾
const char* crlf = buf->findCRLF();
if (crlf)
{
//解析请求行
ok = processRequestLine(buf->peek(), crlf);
if (ok) //如果成功,设置请求行事件
{
//设置请求时间
request_.setReceiveTime(receiveTime);
//将请求行从buf中取回,包括\r\n
buf->retrieveUntil(crlf + 2);
state_ = kExpectHeaders; //将Httpontext状态改为KexpectHeaders状态
}
else
{
hasMore = false;
}
}
else
{
hasMore = false;
}
}
else if (state_ == kExpectHeaders) //处于Header状态
{
const char* crlf = buf->findCRLF();
if (crlf)
{
const char* colon = std::find(buf->peek(), crlf, ':'); //查找:
if (colon != crlf)
{
//找到添加头部,加到map容器
request_.addHeader(buf->peek(), colon, crlf);
}
else
{
// empty line, end of header
// FIXME:
state_ = kGotAll; //一旦请求完毕,再也找不到':'了,状态改为gotall状态,循环退出
hasMore = false;
}
buf->retrieveUntil(crlf + 2); //请求完毕也把crlf取回
}
else
{
hasMore = false;
}
}
else if (state_ == kExpectBody)
{
// FIXME:
}
}
return ok;
}
它先是解析请求行,如果有错has_more为false会跳出循环,后面代码解析所有的头部数据,有错也会跳出循环。
bool HttpContext::processRequestLine(const char* begin, const char* end)
{
bool succeed = false;
const char* start = begin;
const char* space = std::find(start, end, ' '); //查找空格
//格式 : GET / HTTP/1.1
//找到GET并设置请求方法
if (space != end && request_.setMethod(start, space))
{
start = space+1;
space = std::find(start, end, ' '); //再次查找
if (space != end) //找到
{
const char* question = std::find(start, space, '?');
if (question != space) //找到了'?',说明有请求参数
{
//设置路径
request_.setPath(start, question);
//设置请求参数
request_.setQuery(question, space);
}
else
{
//没有找到只设置路径
request_.setPath(start, space);
}
start = space+1;
//查找有没有"HTTP/1."
succeed = end-start == 8 && std::equal(start, end-1, "HTTP/1.");
if (succeed) //如果成功,判断是采用HTTP/1.1还是HTTP/1.0
{
if (*(end-1) == '1')
{
request_.setVersion(HttpRequest::kHttp11);
}
else if (*(end-1) == '0')
{
request_.setVersion(HttpRequest::kHttp10);
}
else
{
succeed = false; //请求行失败
}
}
}
}
return succeed;
}
好的,请求行一旦解析完毕,如果失败会关闭连接,成功的化onMessage()函数会继续进行,开始执行onRequest()函数处理请求:
void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequest& req)
{
//取出头部
const string& connection = req.getHeader("Connection");
// 如果connection为close或者1.0版本不支持keep-alive,标志着我们处理完请求要关闭连接
bool close = connection == "close" ||
(req.getVersion() == HttpRequest::kHttp10 && connection != "Keep-Alive");
//使用close构造一个HttpResponse对象,该对象可以通过方法.closeConnection()判断是否关闭连接
HttpResponse response(close);
//typedef boost::function HttpCallback;
//执行用户注册的回调函数
httpCallback_(req, &response);
Buffer buf; //用户处理后的信息,追加到缓冲区
response.appendToBuffer(&buf);
conn->send(&buf); //发送数据
if (response.closeConnection()) //如果关闭
{
conn->shutdown(); //关了它
}
}
class HttpRequest : public muduo::copyable
{
public:
enum Method //请求方法
{
kInvalid, kGet, kPost, kHead, kPut, kDelete
};
enum Version //协议版本
{
kUnknown, kHttp10, kHttp11
};
HttpRequest()
: method_(kInvalid),
version_(kUnknown)
{
}
//设置版本
void setVersion(Version v)
{
version_ = v;
}
Version getVersion() const
{ return version_; }
//设置方法,
bool setMethod(const char* start, const char* end)
{
assert(method_ == kInvalid);
//使用字符串首尾构造string,不包括尾部,如char *s="123", string s=(s,s+3),则s输出为123
string m(start, end);
if (m == "GET")
{
method_ = kGet;
}
else if (m == "POST")
{
method_ = kPost;
}
else if (m == "HEAD")
{
method_ = kHead;
}
else if (m == "PUT")
{
method_ = kPut;
}
else if (m == "DELETE")
{
method_ = kDelete;
}
else
{
method_ = kInvalid;
}
return method_ != kInvalid;
}
//返回请求方法
Method method() const
{ return method_; }
//请求方法转换成字符串
const char* methodString() const
{
const char* result = "UNKNOWN";
switch(method_)
{
case kGet:
result = "GET";
break;
case kPost:
result = "POST";
break;
case kHead:
result = "HEAD";
break;
case kPut:
result = "PUT";
break;
case kDelete:
result = "DELETE";
break;
default:
break;
}
return result;
}
//设置路径
void setPath(const char* start, const char* end)
{
path_.assign(start, end);
}
const string& path() const
{ return path_; }
//
void setQuery(const char* start, const char* end)
{
query_.assign(start, end);
}
const string& query() const
{ return query_; }
//设置接收时间
void setReceiveTime(Timestamp t)
{ receiveTime_ = t; }
Timestamp receiveTime() const
{ return receiveTime_; }
//添加头部信息,客户传来一个字符串,我们把它转化成field: value的形式
void addHeader(const char* start, const char* colon, const char* end)
{
string field(start, colon); //header域
++colon;
//去除左空格
while (colon < end && isspace(*colon))
{
++colon;
}
string value(colon, end); //heade值
//去除右空格,如果右边有空格会一直resize-1
while (!value.empty() && isspace(value[value.size()-1]))
{
value.resize(value.size()-1);
}
//std::map headers_;
headers_[field] = value;
}
//根据头域返回值
string getHeader(const string& field) const
{
string result;
std::map::const_iterator it = headers_.find(field);
if (it != headers_.end())
{
result = it->second;
}
return result;
}
//返回头部
const std::map& headers() const
{ return headers_; }
//交换
void swap(HttpRequest& that)
{
std::swap(method_, that.method_);
path_.swap(that.path_);
query_.swap(that.query_);
receiveTime_.swap(that.receiveTime_);
headers_.swap(that.headers_);
}
private:
Method method_; //请求方法
Version version_; //请求版本
string path_; //请求路径
string query_; //请求参数
Timestamp receiveTime_; //请求接收时间
std::map headers_; //header列表
};
这个类的作用就是示例代码中那样,设置响应的各种信息,包括头部,体部,示例代码中这样用的:
resp->setStatusCode(HttpResponse::k200Ok); //状态码200
resp->setStatusMessage("OK"); //ok
resp->setContentType("text/html"); //html文本
resp->addHeader("Server", "Muduo"); //增加头部
string now = Timestamp::now().toFormattedString(); //生成时间戳
resp->setBody("This is title " //标题
"Hello
Now is " + now + //内容
"");
void HttpServer::onRequest(const TcpConnectionPtr& conn, const HttpRequest& req)
{
//取出头部
const string& connection = req.getHeader("Connection");
// 如果connection为close或者1.0版本不支持keep-alive,标志着我们处理完请求要关闭连接
bool close = connection == "close" ||
(req.getVersion() == HttpRequest::kHttp10 && connection != "Keep-Alive");
//使用close构造一个HttpResponse对象,该对象可以通过方法.closeConnection()判断是否关闭连接
HttpResponse response(close);
//typedef boost::function HttpCallback;
//执行用户注册的回调函数
httpCallback_(req, &response);
Buffer buf; //用户处理后的信息,追加到缓冲区
response.appendToBuffer(&buf);
conn->send(&buf); //发送数据
if (response.closeConnection()) //如果关闭
{
conn->shutdown(); //关了它
}
}