从零实现http:(1)以oop的方式实现http

一、http报文结构:在这里插入图片描述在这里插入图片描述
二、具体实现

int main(){
    HttpServer server(9999);//监听端口9999
    server.start_listen();
    return 0;
}

httpserver.h

class HttpServer{
public:
    HttpServer();
    HttpServer(u_short p);
    inline int get_sock_id(){ return server_sock; };
    inline u_short get_port(){ return port; };
    void start_listen();//建立、绑定、监听端口
    static void accept_request(int client_sock,HttpServer* t);//线程函数,http解析函数
private:
    int server_sock;
    u_short port;
    string baseURL;

    void startup();
};

httpserver.cpp

HttpServer::HttpServer(){
    baseURL = "~/workspace/MiniHttpd/hello.txt";
    port=0;
    startup();
}

HttpServer::HttpServer(u_short p){
    baseURL = "~/workspace/MiniHttpd/hello.txt";
    port=p;
    startup();
}

void HttpServer::startup(){
    int on = 1;
    struct sockaddr_in name;

    server_sock = socket(PF_INET, SOCK_STREAM, 0);    //使用TCP协议
    if (server_sock == -1)
        cerr<<"[ERROR]: create socket failed"<<endl;
    memset(&name, 0, sizeof(name));
    name.sin_family = AF_INET;
    name.sin_port = htons(port);
    name.sin_addr.s_addr = htonl(INADDR_ANY);
    if ((setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)  
    {  
        cerr<< "[ERROR]: setsockopt failed"<<endl;
    }
    if (bind(server_sock, (struct sockaddr *)&name, sizeof(name)) < 0)
        cerr<<"[ERROR]: bind failed"<<endl;

    if (port == 0)  /* if dynamically allocating a port */
    {
        socklen_t namelen = sizeof(name);
        if (getsockname(server_sock, (struct sockaddr *)&name, &namelen) == -1)
            cerr<<"[ERROR]: getsockname failed"<<endl;
        port = ntohs(name.sin_port);   //函数使用指针,就是为了这里可以将动态分配的端口返回给函数外部
    }
    if (listen(server_sock, 5) < 0)   //listen第二个参数为连接请求队列长度,5代表最多同时接受5个连接请求
        cerr<<"[ERROR]: listen failed"<<endl;
}

void HttpServer::start_listen(){
    cout<<"httpd running on port "<<port<<endl;
    int client_sock = -1;
    struct sockaddr_in client_name;
    socklen_t  client_name_len = sizeof(client_name);
    pthread_t newthread;

    while (1)
    {
        //accept函数用来保存请求客户端的地址相关信息
        client_sock = accept(server_sock,
                (struct sockaddr *)&client_name,
                &client_name_len);
        if (client_sock == -1)
            cerr<<"[ERROR]: accept failed"<<endl;

        thread accept_thread(accept_request,client_sock,this);
        accept_thread.join();
    }

    close(server_sock);
}

void HttpServer::accept_request(int client_sock, HttpServer* t)
{
    int client = client_sock;
    char buf[1024];
    size_t numchars;
    char method[255];
    char url[255];
    char path[512];
    size_t i, j;
    struct stat st;
    int cgi = 0;      /* becomes true if server decides this is a CGI
                       * program */
    char *query_string = NULL;

    read(client_sock,(void*)buf,1024);
    string req(buf);
    HttpRequest request(req);
    cout<<"url: "<<request.get_url()<<endl;
    string req_url = t->baseURL+ request.get_url();
    //debug
    cout<<"req_url"<<req_url<<endl;
    auto header = request.get_header();
    cout<<"[GET REQUEST]: Host = "<<header.find("Host")->second<<endl;

    HttpResponse response(200);
    response.load_from_file(req_url);
    response.Content_Type = "text/html";
    string res_string = response.get_response();
    // cout<<res_string<<endl;
    send(client,res_string.c_str(),strlen(res_string.c_str()),0);
    close(client);
}

httpresponse.h

class HttpResponse{
public:
    HttpResponse(int st);
    void set_header(string key, string val);    //设置头部自定义字段
    void load_from_file(string url);
    string get_response();

    /* 基础头部字段,供快速填充,自定义字段需手动设置 */
    string Allow;               //允许的方法,get,post
    string Content_Encoding;    //文档的编码
    string Content_Length;      //表示内容长度
    string Content_Type;        //文档类型
    string Expires;             //过期时间
    string Last_Modified;       //最后一次修改
    string Location;            //重定向RUL
    string Refresh;             //在多久后刷新文档
    string Set_Cookie;          //设置相关cookie
    string WWW_Authenticate;    //授权信息
    
private:
    string version;                     //http版本
    string status;                      //http状态码
    string date;                        //response生成时间
    string server;                      //http服务器名称
    map<string,string> custom_header;   //自定义头部字段
    string generate_header();           //使用全部信息组装HTTP Response头部
    string response_body;               //返回内容体
};

httpresponse.cpp

string simplePage(){
    string page;
    page += "<html>";
    page += "<head>";
    page += "<title>404 page not found</title>";
    page += "</head>";
    page += "<body>";
    page += "<h2> 404 Page Not Found </h2>";
    page += "</body>";
    page += "</html>";
    return page;
}

string GetGmtTime()
{
    time_t rawTime;
    struct tm* timeInfo;
    char szTemp[30]={0};
    time(&rawTime);
    timeInfo = gmtime(&rawTime);
    strftime(szTemp,sizeof(szTemp),"%a, %d %b %Y %H:%M:%S GMT",timeInfo);
    string GmtTime(szTemp);
    return GmtTime;
}

HttpResponse::HttpResponse(int st){
    version = "HTTP/1.1";
    status = to_string(st);
    date = GetGmtTime();
    server = "Minihttpd";
}

void HttpResponse::set_header(string key, string val){
    custom_header.insert(pair<string,string>(key,val));
}

void HttpResponse::load_from_file(string url){
    ifstream in_file(url);
    if(in_file.fail()){
        cerr<<"[404]: "<<url<<" not found"<<endl;
        response_body=simplePage();
        return;
    }
    stringstream buffer;
    buffer << in_file.rdbuf();
    response_body = buffer.str();
    in_file.close();
}
//返回的报文整体内容
string HttpResponse::get_response(){
    string response;
    response += generate_header();
    response += response_body;
    return response;
}

string HttpResponse::generate_header(){
    string header;
    header += version += " ";
    header += status += "\n";
    (header += "Date:") += date += "\n";

    if(Allow.size()!=0)
        (header += "Allow:") += Allow += "\n";
    if(Content_Encoding.size()!=0)
        (header += "Content-Encoding:") += Content_Encoding += "\n";
    if(Content_Length.size()!=0)
        (header += "Content-Length:") += Content_Length += "\n";
    if(Content_Type.size()!=0)
        (header += "Content-Type:") += Content_Type += "\n";
    if(Expires.size()!=0)
        (header += "Expires:") += Expires += "\n";
    if(Last_Modified.size()!=0)
        (header += "Last-Modified:") += Last_Modified += "\n";
    if(Location.size()!=0)
        (header += "Location:") += Location += "\n";
    if(Refresh.size()!=0)
        (header += "Refresh:") += Refresh += "\n";
    if(Set_Cookie.size()!=0)
        (header += "Set-Cookie:") += Set_Cookie += "\n";
    if(WWW_Authenticate.size()!=0)
        (header += "WWW-Authenticate:") += WWW_Authenticate += "\n";
    
    //基本字段添加完成后,填充用户自定义字段
    for(auto i:custom_header){
        ((header += i.first) += ":") += i.second += "\n";
    }

    header += "\r\n";
    return header;
}

httprequest.h

class HttpRequest{
public:
    HttpRequest(string raw_data);
    inline const string get_method(){ return method; };
    inline const string get_url(){ return url; };
    inline const map<string,string>& get_header(){ return header; };
private:
    string method;  //该http请求方法
    string url;     //请求URL
    string version; //http版本,实现的是http1.1
    map<string,string> header;
};

httprequest

std::vector<std::string> splitString(std::string srcStr, std::string delimStr,bool repeatedCharIgnored)
{
    std::vector<std::string> resultStringVector;
    std::replace_if(srcStr.begin(), srcStr.end(), [&](const char& c){if(delimStr.find(c)!=std::string::npos){return true;}else{return false;}}/*pred*/, delimStr.at(0));//将出现的所有分隔符都替换成为一个相同的字符(分隔符字符串的第一个)
    size_t pos=srcStr.find(delimStr.at(0));
    std::string addedString="";
    while (pos!=std::string::npos) {
        addedString=srcStr.substr(0,pos);
        if (!addedString.empty()||!repeatedCharIgnored) {
            resultStringVector.push_back(addedString);
        }
        srcStr.erase(srcStr.begin(), srcStr.begin()+pos+1);
        pos=srcStr.find(delimStr.at(0));
    }
    addedString=srcStr;
    if (!addedString.empty()||!repeatedCharIgnored) {
        resultStringVector.push_back(addedString);
    }
    return resultStringVector;
}

HttpRequest::HttpRequest(string raw_data){
    vector<string> lines = splitString(raw_data,"\n",false);

    vector<string> first_line = splitString(lines[0]," ",false);
    if(first_line.size()==3){
        method = first_line[0];
        url = first_line[1];
        version = first_line[2];
    }
    else{
        cerr<<"bad http request when get method"<<endl;
    }

    for(size_t i = 1;i<lines.size();i++){
        if(lines[i]==""||lines[i]=="\r")
        {
            // cout<<"http header trans finished"<<endl;
            break;
        }
        size_t pos = lines[i].find_first_of(':');
        if(pos==string::npos){
            cerr<<"bad request"<<endl;
            cout<<"this lines ascii is"<<endl;
            for(auto j:lines[i]){
                cout<<(int)(j)<<" ";
            }
            cout<<endl;
            break;
        }
        string key = lines[i].substr(0,pos);
        string val;
        //去除val前可能存在的空格
        if(lines[i][pos+1]==' '){
            val = lines[i].substr(pos+2);
        }
        else{
            val = lines[i].substr(pos+1);
        }
        // cout<<key<<"\t"<<val<<endl;
        header.insert(pair<string,string>(key,val));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值