一、接口
添加请求--处理函数映射消息(GET/POST/PUT/DELETE)
设置静态资源根目录
设置是否启动超时连接关闭
设置线程池中线程的数量
启动服务器
OnConnected -- 用于给TcpServer设置协议上下文
OnMessage -- 用于进行缓冲区数据解析处理
请求的路由查找
静态资源请求查找和处理
功能性请求
组织响应进行回复
二、成员变量

cpp
using PtrConnection = std::shared_ptr<Connection>;
using Hander = std::function<void(const HttpResquest&,HttpResponse*)>;
using Handers = std::vector<std::pair<std::regex,Hander>>;
Handers _get_route;
Handers _post_route;
Handers _put_route;
Handers _delete_route;
std::string _basedir;//静态资源根目录
TcpServer _server;
三、成员函数
构造

开启TcpServer的非活跃销毁,设置事件,设置Tcp的连接完成事件回调,信息事件回调
连接完成事件回调
设置上下文

cpp
void OnConnected(const PtrConnection& conn)
{
conn->SetContext(HttpContext());
DBG_LOG("NEW CONNECTION %p", conn.get());
}
信息事件回调

1、获取上下文
2、通过上下文对缓冲区数据进行解析,得到HttpRequest对象
如果缓冲区的数据解析出错,就直接回复出错响应(进行错误响应,关闭连接,填充一个错误显示页面数据发送到rsp中,组织响应发送给客户端)
如果解析正常,且请求已经获取完毕,才开始去进行处理
如果当前请求还没有接收完整,则退出,等新的数据到来重新继续处理
3、请求路由+业务处理
4、对HttpResponse进行组织发送
5、重置上下文
6、根据长短连接是否关闭连接或继续处理(短链接则直接关闭)
cpp
void OnMessage(const PtrConnection& conn, Buffer*buffer)
{
while(buffer->ReadAblesize()>0)
{
HttpContext *context = conn->GetContext()->get<HttpContext>();
context->RecvHttpRequest(buffer);
HttpResquest &req = context->Requst();
HttpResponse rsp(context->RespStatu());
if(context->RespStatu()>=404)
{
ErrorHandler(req,&rsp);
WriteResponse(conn,req,rsp);
conn->Shutdown();
context->ReSet();
buffer->MoveReadoffset(buffer->ReadAblesize());
return;
}
if(context->Recvstatu()!=RECV_HTTP_OVER)
{
return;
}
Route(req,&rsp);
WriteResponse(conn,req,rsp);
context->ReSet();
if(rsp.Close()==true)
{
conn->Shutdown();
}
}
return;
}
错误页面书写

cpp
void ErrorHandler(HttpResquest&req,HttpResponse*rsp)
{
std::string body;
body += "<html>";
body += "<head>";
body += "<meta http-equiv='Content-Type'content = 'text/html;charset=utf-8' > ";
body += "</head>";
body += "<body>";
body += "<h1>";
body += std::to_string(rsp->_statu);
body += " ";
body += Util::StatuDesc(rsp->_statu);
body += "</h1>";
body += "</body>";
body += "</html>";
rsp->SetContent(body,"text/html");
}
响应

1、先完善头部字段
2、将rsp中的要素,按照http协议格式进行组织
3、发送数据
cpp
void WriteResponse(const PtrConnection& conn,HttpResquest&req,HttpResponse&rsp)
{
if(req.Close() == true)
{
rsp.SetHeader("Connection","close");
}
else
{
rsp.SetHeader("Connection","keep-alive");
}
if(req._body.empty()==false&&req.HasHeader("Content-Length")==false)
{
rsp.SetHeader("Content-Length",std::to_string(rsp._body.size()));
}
if(req._body.empty()==false&&req.HasHeader("Content-Type")==false)
{
rsp.SetHeader("Content-Type", "application/octet-stream");
}
if (rsp._redirect_flag == true)
{
rsp.SetHeader("Location", rsp._redirect_url);
}
std::stringstream rsp_str;
rsp_str << req._version << " " << std::to_string(rsp._statu) << " "<< Util::StatuDesc(rsp._statu) << "\r\n";
for (auto &head : rsp._headers)
{
rsp_str << head.first << ": " << head.second << "\r\n";
}
rsp_str << "\r\n";
rsp_str << rsp._body;
conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
}
请求分辨

1、对请求进行分辨,是一个静态资源请求,还是一个功能性请求
静态资源请求,则进行静态资源的处理
功能性请求,则需要通过路由表来确定是否有处理函数
既不是静态资源请求,也没有设置对应的功能性请求处理函数,返回405
cpp
void Route(HttpResquest&req,HttpResponse*rsp)
{
std::cout << req._path<<std::endl;
if(IsFileHandler(req))//是否是静态资源访问
{
return FileHander(req,rsp);
}
if(req._method=="GET"||req._method=="HEAD")
{
std::cout << "GET" << std::endl;
return Dispatcher(req,rsp,_get_route);
}
if(req._method=="POST")
{
return Dispatcher(req,rsp,_post_route);
}
if(req._method=="PUT")
{
return Dispatcher(req,rsp,_put_route);
}
if(req._method=="DELETE")
{
return Dispatcher(req,rsp,_delete_route);
}
rsp->_statu = 405;
return;
}
判断是否是一个静态资源请求

1、必须设置了静态资源根目录
2、请求方法必须是 GET 或者HEAD请求方法
3、请求的资源必须是一个合法路径
4、请求的资源必须存在,且是一个普通文件
如果请求是/需要将他进行转化
cpp
void FileHander(HttpResquest&req,HttpResponse*rsp)
{
std::string req_path = _basedir + req._path;
if(req_path.back()=='/')
{
req_path+="index.html";
}
bool ret = Util::ReadFile(req_path,&rsp->_body);
if(ret == false)
{
return;
}
std::string mime = Util::ExtMime(req._path);
rsp->SetHeader("Content-Type",mime);
return;
}
静态资源的请求处理
将静态资源文件的数据读取出来,放到rsp的_body中,并设置mime

cpp
void FileHander(HttpResquest&req,HttpResponse*rsp)
{
std::string req_path = _basedir + req._path;
if(req_path.back()=='/')
{
req_path+="index.html";
}
bool ret = Util::ReadFile(req_path,&rsp->_body);
if(ret == false)
{
return;
}
std::string mime = Util::ExtMime(req._path);
rsp->SetHeader("Content-Type",mime);
return;
}
功能性请求的分类处理

在对应的请求方法的路由表中,查找是否含有对应资源请求的处理函数,有则调用,没有则404
思想: 路由表储存的键值对 --- 正则表达式 & 处理函数
使用正则表达式,对请求的资源路径进行正则匹配,匹配成功就使用对应函数进行处理
cpp
void Dispatcher(HttpResquest&req,HttpResponse*rsp,Handers& handlers)
{
for(auto&handler:handlers)
{
const std::regex& re = handler.first;
auto it = handler.first;
const Hander& functor = handler.second;
bool ret = std::regex_match(req._path,req._matches,re);
if(ret == false)
{
continue;
}
return functor(req,rsp);
}
rsp->_statu=404;
}
设置函数

cpp
void Get(const std::string pattern,const Hander&handler)
{
std::cout << std::endl;
_get_route.push_back(std::make_pair(std::regex(pattern),handler));
}
void Post(const std::string pattern,const Hander&handler)
{
_post_route.push_back(std::make_pair(std::regex(pattern),handler));
}
void Delete(const std::string pattern,const Hander&handler)
{
_delete_route.push_back(std::make_pair(std::regex(pattern),handler));
}
void Put(const std::string pattern,const Hander&handler)
{
_put_route.push_back(std::make_pair(std::regex(pattern),handler));
}
设置静态路径

cpp
void SetBaseDir(const std::string path)
{
assert(Util::IsDirectory(path) == true);
_basedir = path;
}
设置线程个数

cpp
void SetThreadCount(int count)
{
_server.SetThreadCount(count);
}
设置监听状态

cpp
void Listen()
{
_server.Start();
}