需要注意,服务器响应时需要构建HTTP响应,响应网页作为响应HTTP的正文。复习见 Linux_网络_应用层协议 http/https
1. 填充HTTP响应
状态码描述采用函数来获取,函数内部是通过switch来判断状态码返回对应的字符串。
HTTP响应正文是网页,这里采用sendfile直接将内核的文件拷贝到网卡上,避免了向内核拷贝提高效率。 out_fd:输出到这个文件描述符中(套接字)。 in_fd:从这个文件描述符读(需要发送的文件描述符) offset:默认设置为0。 count:需要传输多少个字节。
注意:传输字节数在分析HTTP响应stat函数判断请求路径是否合法时已经获取到了(stat函数中st_size字段),将这个状态保存到HTTP响应字段方便服务器发送网页给客户端。
其次,响应要至少包括Content-Length和Content-Type这两个字段。
文件类型之前被保存在请求结构体中的路径上,只要进行字符串截取即可。HTTP类型与后缀的对照关系来填充Content-Type字段。
完整代码
#pragma once
#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include"Util.h"
#include<string>
#include<vector>
#include"Log.h"
#include<sstream>
#include<unordered_map>
#include<sys/stat.h>
#include<algorithm>
#include<sys/sendfile.h>
#include<fcntl.h>
#define OK 200
#define NOTFOUND 404
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define VERSON_HTTP "HTTP/1.0"
#define LINE_END "\r\n"
static std::string CodeToInfo(int code){
std::string Info;
switch(code){
case 200:
Info="OK";
break;
case 404:
Info="NotFound";
break;
default:
break;
}
return Info;
}
class HttpResponse{
public:
std::string StatusLine_HTTP;
std::vector<std::string>ResponHeads;
std::string ResponBlank=LINE_END;
std::string ResponBody;
int status_code=OK;
int fd=-1;
size_t size;
};
class HttpRequest{
public:
std::string RequestLine_HTTP;
std::vector<std::string>RequestHeads;
std::string RequestBlank;
std::string RequestBody;
std::string Method;
std::string URI;
std::string Version;
std::unordered_map<std::string,std::string>Head_KVS;
int Content_Lenth=0;
std::string Path;
std::string Param;
bool CGI=false;
std::string Type;
};
class EndPoint{
private:
int sock;
HttpRequest http_request;
HttpResponse http_response;
private:
void GetHttpRequestLine(){
Util::ReadLine(sock,http_request.RequestLine_HTTP);
http_request.RequestLine_HTTP.pop_back();
ERRORLOG(INFO,http_request.RequestLine_HTTP);
}
void GetHttpRequstHeads(){
std::string line;
while(true){
Util::ReadLine(sock,line);
if(line=="\n"){
ERRORLOG(INFO,line);
http_request.RequestBlank=line;
break;
}
line.pop_back();
http_request.RequestHeads.push_back(line);
ERRORLOG(INFO,line);
line.clear();
}
}
void AnalyQuestLine(){
std::stringstream Str(http_request.RequestLine_HTTP);
Str>>http_request.Method>>http_request.URI>>http_request.Version;
std::string& strtmp=http_request.Method;
std::transform(strtmp.begin(),strtmp.end(),strtmp.begin(),::toupper);
}
void AnalyuestHeadS(){
std::string key;
std::string value;
for(auto&line:http_request.RequestHeads){
if(Util::CutString(line,key,value,": ")){
http_request.Head_KVS.insert(std::make_pair(key,value));
}
else{
ERRORLOG(FATA,"AnalyuestHeadS error");
}
}
}
bool HaveHttpBody(){
std::string& Method=http_request.Method;
if(Method=="POST"){
std::unordered_map<std::string,std::string>::iterator iter=http_request.Head_KVS.find("Content-Lenth");
if(iter!=http_request.Head_KVS.end()){
http_request.Content_Lenth=atoi(iter->second.c_str());
return true;
}
}
return false;
}
void GetHttpBody(){
if(HaveHttpBody()){
int Content_Lenth=http_request.Content_Lenth;
char ch=0;
while(Content_Lenth>0){
ssize_t size=recv(sock,&ch,1,0);
if(size>0){
http_request.RequestBody.push_back(ch);
Content_Lenth--;
}
else{
break;
}
}
}
}
int ProceNoCGI(size_t size){
http_response.fd=open(http_request.Path.c_str(),O_RDONLY);
if(http_response.fd>0){
std::string& Str=http_response.StatusLine_HTTP;
Str+=VERSON_HTTP;
Str+=" ";
Str+=std::to_string(http_response.status_code);
Str+=" ";
Str+=CodeToInfo(http_response.status_code);
Str+=LINE_END;
http_response.size=size;
std::string Content_Lenth_str="Content-Lenth: ";
Content_Lenth_str+=std::to_string(size);
Content_Lenth_str+=LINE_END;
http_response.ResponHeads.push_back(Content_Lenth_str);
std::string Content_Type_str="Content-Type: ";
Content_Type_str+=Util::SuffixToDesc(http_request.Type);
Content_Type_str+=LINE_END;
http_response.ResponHeads.push_back(Content_Type_str);
return OK;
}
return NOTFOUND;
}
public:
EndPoint(int _sock):sock(_sock){}
void RecvQuest_HTTP(){
GetHttpRequestLine();
GetHttpRequstHeads();
}
void AnalyQuest_HTTP(){
AnalyQuestLine();
AnalyuestHeadS();
GetHttpBody();
}
void MakeRespon_HTTP(){
std::string tmpPath;
size_t size =0;
struct stat stat_buff;
size_t suffix_pos=0;
if(http_request.Method!="GET"&&http_request.Method!="POST"){
ERRORLOG(WARNING,"error request");
http_response.status_code=NOTFOUND;
goto END;
}
if(http_request.Method=="GET"){
size_t pos=http_request.URI.find('?');
if(pos!=std::string::npos){
Util::CutString(http_request.URI,http_request.Path,http_request.Param,"?");
http_request.CGI=true;
}
else{
http_request.Path=http_request.URI;
}
}
else if(http_request.Method=="POST"){
http_request.CGI=true;
}
tmpPath=http_request.Path;
http_request.Path=WEB_ROOT;
http_request.Path+=tmpPath;
if(http_request.Path[http_request.Path.size()-1]=='/'){
http_request.Path+=HOME_PAGE;
}
if(stat(http_request.Path.c_str(),&stat_buff)==0){
if(S_ISDIR(stat_buff.st_mode)){
http_request.Path+="/";
http_request.Path+=HOME_PAGE;
stat(http_request.Path.c_str(),&stat_buff);
}
if((stat_buff.st_mode &S_IXUSR)||(stat_buff.st_mode &S_IXGRP)||(stat_buff.st_mode& S_IXOTH)){
http_request.CGI=true;
}
size=stat_buff.st_size;
}
else{
ERRORLOG(WARNING,http_request.Path+" Not Found!");
http_response.status_code=NOTFOUND;
goto END;
}
suffix_pos=http_request.Path.rfind(".");
if(suffix_pos==std::string::npos){
http_request.Type=".html";
}
else{
http_request.Type=http_request.Path.substr(suffix_pos);
}
if(http_request.CGI==true){
}
else{
http_response.status_code=ProceNoCGI(size);
}
END:
if(http_response.status_code!=OK){
}
return;
}
void SendRespon_HTTP(){
send(sock,http_response.StatusLine_HTTP.c_str(),http_response.StatusLine_HTTP.size(),0);
for(size_t size=0;size<http_response.ResponHeads.size();size++){
send(sock,http_response.ResponHeads[size].c_str(),http_response.ResponHeads[size].size(),0);
}
send(sock,http_response.ResponBlank.c_str(),http_response.ResponBlank.size(),0);
sendfile(sock,http_response.fd,nullptr,http_response.size);
close(http_response.fd);
}
~EndPoint(){}
};
class Entry{
public:
static void*SolveQuest(void*_sock){
ERRORLOG(INFO,"Processing Requests...");
int sock=*(int*)_sock;
delete(int*)_sock;
EndPoint* endpoint=new EndPoint(sock);
endpoint->RecvQuest_HTTP();
endpoint->AnalyQuest_HTTP();
endpoint->MakeRespon_HTTP();
endpoint->SendRespon_HTTP();
delete endpoint;
ERRORLOG(INFO,"Processing Request End!");
return nullptr;
}
};
|