目录
1.构建响应
- 构建响应流程如下 - 确认方法- 根据不同方法,以不同方法提参- 确认访问资源
- 如果用户的URL没有指明要访问的某种资源(路径),虽然浏览器默认会添加/,但是依旧没有告知服务器,要访问什么资源 - 此时,默认返回对应服务的首页- 这里的**/**不是Linux服务器的根目录,通常是http服务器设置的自己的WEB根目录
- 关于stat- stat()返回一个文件的信息,信息都存在一个struct stat结构体变量中 - 细节可以:
man 2 stat
- struct stat可以判断 - 是否是一个可执行程序- 该文件的大小- … - 判断文件是否是可执行程序 - 若不是,则走
ProcessNonCgi
逻辑,返回静态网页- 若是,则走ProcessCgi
逻辑,将参数传递给子程序处理 - 请求资源是什么类型,取决于资源的后缀
- 为什么要使用goto?- 分析协议的每一步,都有可能出错- 所以在做出错处理的时候,如果没有goto,会导致使用大量的if判断
voidBuildResponse(){
size_t pos =0;if(_request.method !="GET"&& _request.method !="POST"){// 非法请求
_response.status_code = BAD_REQUEST;// TODOLOG(WARNING,"Method is not right");goto END;}if(_request.method =="GET"){// GET可能带参数,也可能不带参数,要区分出来if(_request.uri.find('?')!= std::string::npos){Util::CutString(_request.uri, _request.path, _request.arg,"?");
_request.cgi =true;}else{
_request.path = _request.uri;}}elseif(_request.method =="POST"){
_request.cgi =true;
_request.path = _request.uri;}else{// Do Nothing}
_request.path.insert(0, WEB_ROOT);// 从根目录开始// 如果访问的是根目录,则默认访问主页if(_request.path[_request.path.size()-1]=='/'){
_request.path += HOME_PAGE;}// 确认访问资源是否存在structstat st;if(stat(_request.path.c_str(),&st)==0)// TODO 待整理stat(){// 访问的是否是一个具体资源?if(S_ISDIR(st.st_mode)){// 请求的是一个目录,需要特殊处理 -- 改为访问该目录下主页// 虽然是目录,但是绝对不会以/结尾
_request.path +="/";
_request.path += HOME_PAGE;// 上面更改了path指向,所以重新获取文件状态stat(_request.path.c_str(),&st);}// 是否是一个可程序程序?if(st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH){
_request.cgi =true;// TODO CGI标志位感觉有多余}
_response.fSize = st.st_size;}else{// 资源不存在LOG(WARNING, _request.path +" Not Found");
_response.status_code = NOT_FOUND;goto END;}// 读取文件后缀
pos = _request.path.rfind('.');if(pos== std::string::npos){
_request.suffix =".html";// 没找到就默认设置为html}else{
_request.suffix = _request.path.substr(pos);}if(_request.cgi){
_response.status_code =ProcessCgi();// 执行目标程序,拿到结果到_response.response_body}else{// 1.至此,目标网页一定是存在的// 2.返回并不是单单返回网页,而是要构建HTTP响应
_response.status_code =ProcessNonCgi();// 简单的网页返回,返回静态网页,只需要打开即可}
END:// 最后统一构建响应BuildResponseHelper();return;}voidBuildResponseHelper(){// 此处status_line是干净的,没有内容的// 构建状态行
_response.status_line += HTTP_VERSION;
_response.status_line +=" ";
_response.status_line += std::to_string(_response.status_code);
_response.status_line +=" ";
_response.status_line +=Util::Code2Desc(_response.status_code);
_response.status_line += LINE_END;// 构建响应正文,可能包括响应报头
std::string path = WEB_ROOT;
path +='/';switch(_response.status_code){case OK:BuildOKResponse();break;case NOT_FOUND:
path += PAGE_404;HandlerError(path);break;case BAD_REQUEST:// 暂时先404处理,后面有需要再改
path += PAGE_404;HandlerError(path);break;case SERVER_ERROR:
path += PAGE_404;HandlerError(path);break;default:break;}}voidBuildOKResponse(){// Header
std::string header_line ="Content-Type: ";
header_line +=Util::Suffix2Desc(_request.suffix);
header_line += LINE_END;
_response.response_header.push_back(header_line);
header_line ="Content-Length: ";if(_request.cgi){
header_line += std::to_string(_response.response_body.size());}else{
header_line += std::to_string(_response.fSize);}
header_line += LINE_END;
_response.response_header.push_back(header_line);}
2.发送响应
- 注意:- 分多次发和把所有内容合成一个字符串一次性发送是没区别的- send只是把内容拷贝到发送缓冲区中- 具体什么时候发,一次性发多少,是由TCP决定的
- 此处
_response.response_body
发送逻辑确保了_response.response_body
能全部发完 - 此次请求为
ProcessNonCgi
时,需要从磁盘读取文件内容,再把内容拷贝到发送缓冲区中,这样是效率低下的 -sendfile()
可以在两个文件描述符之间拷贝数据 - 即:sendfile()
可以把文件内容,直接拷贝到Tcp的发送缓冲区中,提高了效率- 这个拷贝实在内核态完成的,所以比read() / write()
高效的多
voidSendResponse(){send(_sock, _response.status_line.c_str(), _response.status_line.size(),0);for(auto& str : _response.response_header){send(_sock, str.c_str(), str.size(),0);}send(_sock, _response.blank.c_str(), _response.blank.size(),0);if(_request.cgi){constchar*start = _response.response_body.c_str();
size_t size =0, total =0;while(total < _response.response_body.size()&&(size =send(_sock, start + total, _response.response_body.size()- total,0)>0)){
total += size;}}else{sendfile(_sock, _response.fd,nullptr, _response.fSize);close(_response.fd);}}
本文转载自: https://blog.csdn.net/qq_37281656/article/details/142207334
版权归原作者 DieSnowK 所有, 如有侵权,请联系我们删除。
版权归原作者 DieSnowK 所有, 如有侵权,请联系我们删除。