完整http服务器

目录

  1. 背景
  2. 目标
  3. 描述
  4. 技术特点
  5. 开发环境
  6. WWW
  7. 客户端浏览发展史
  8. 服务端http发展史
  9. http分层概览

背景

http协议被广泛使用,从移动端,pc浏览器,http无疑是打开互联网应用窗口的重要协议,http在网络应用层中的地位不可撼动,是能准确区分前后的重要协议、

目标

对http协议的理论学习,从零开始完成web服务器开发,作用下三层协议,从技术到应用,让网络难点无处遁形

描述

采用C/S模型,编写支持中小型应用的http,并结合mysql,理解常见互联网应用行为,完全理解从上网开始,到关闭浏览器的所有操作中的技术细节

技术特点

网络编程(TCP/IP协议,socket流式套接字,http协议)
多线程技术
cgi技术
线程池

开发环境

cents 7/ubuntu 20.04 + vim/gcc/gdb+c/c++

WWW

www是环球信息网的缩写,(亦作“Web”、“WWW”、“W3”,英文全称为“World Wide Web”),中文名字‘万维网,“环球网”’等,常简称为Web

分为Web客户端和Web服务器程序。www可以让Web客户端(常用浏览器)访问浏览Web的页面。是一个由许多互相连接的超文本组成的系统,通过互联网访问。在这个系统中,每个有用的事物,称为一样“资源”;并且由一个全局“统一资源标识符”(URI)标识;这些资源通过超文本传输协议(Hypertext Transfer protocol)传送给用户,而后者通过点击链接来获得资源

万维网联盟(World Wide Web Consortium,简称W3C),又称W3C理事会。1994年10月在麻省理工学院计算机科学实验室成立。万维网联盟的创始人是万维网的发明者蒂姆·博纳斯-李。–摘自这里

客户端浏览发展史

1990年11月,世界上第一台Web服务器和Web浏览器诞生
1993年1月,NCSA(美国国家超级计算机应⽤中心NationalCenterfor SupercomputerApplications,简称NCSA)研发html内联显⽰图⽚的浏览器Mosaic,不久windows和苹果mac版的Mosaic相继出

NASAhttpd1.0也差不多这个时期出现
1994年12⽉网景公司NetscapeNavigator1.0(网景领航员)浏览器出现
1995年微软发布IE1.0和2.0
紧随其后,web服务器标准Apache0.2诞⽣
1995年左右,微软和⽹景针对html标准开始打仗
2000年,⽹景衰落
2004年,Mozilla(缩写MF或MoFo,全称Mozilla基⾦会,是为⽀持和领导开源的Mozilla项⽬⽽设⽴的⼀个⾮营利组织)基⾦发布firefox,第⼆次浏览器⼤战⼜开始了
随后,IE发布6,7,8,9,10版本,同步Chrome,Opera,Safari浏览器也开始抢占市场
今天的浏览器格局形成

服务端http发展史

1990年,HTTP/0.9诞⽣
1996年5⽉,HTTP/1.0标准诞⽣,记载于RFC1945,该标准⾄今仍然被使⽤
1997年1⽉,HTTP/1.1问世,是⽬前使⽤的主流http版本
HTTP/2.0正在定制,但要被⼴泛使⽤,仍旧需要较多时间

http分层概览

整体来看

在这里插入图片描述

细节

在这里插入图片描述

http相关协议

TCP
IP
DNS

DNS

在这里插入图片描述

http背景补充

目前主流使用http1.1,按照1.0来实现
http是无状态的,不会保留之前的请求和响应,为了保持状态,引入了cookie

URI、URL、URN

URI,是uniform resource identifier,统⼀资源标识符,⽤来唯⼀的标识⼀个资源
URL,是uniform resource locator,统⼀资源定位符,它是⼀种具体的URI,即URL可以⽤来标识⼀个资源,⽽且还指明了如何locate这个资源。
URN,uniform resource name,统⼀资源命名,是通过名字来标识资源,⽐如mailto:javanet@java.sun.com。

URI是一种抽象的,高层次概括定义统一资源表示,URL和URN是具体的资源表示方式,都是一种URI
URL是URI的子集,任何东西,只要能唯一标识出来,都可以说是URI。如果这个标识可以获取到上述对象的路径,同时也它可以是一个URL,但如果这个标识不提供获取对象的路径,,必然不是URL
URI: /home/index.html
URL: www.xxx.com:/home/index.html

浏览器URL格式

http是基于tcp的连接方式进行网络连接,1.1版本可以持续的长连接机制,绝大多数的web开发,都是构建在http协议上的应用
http url的格式如下:
http表⽰要通过HTTP协议来定位⽹络资源
host表⽰合法的Internet主机域名或者IP地址,本主机IP:127.0.0.1
port指定⼀个端⼝号,为空则使⽤缺省端⼝80
abs_path指定请求资源的URI
如果URL中没有给出abs_path,那么当它作为请求URI时,必须以“/”的形式给出,通常这个⼯作浏览器⾃动帮我们完成。默认访问首页

一个较完整的http请求:
http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1

请求与响应

示意图
在这里插入图片描述

细节

请求格式
在这里插入图片描述
响应格式
在这里插入图片描述

请求的方法

GET:获取资源,获取被URI标识的资源,重点
POST:传输实体主体,重点
PUT:传输文件,指定文件放的uri所标识的路径,类似ftp,有安全问题,大部分web不用
HEAD:获取报文首部,和GET类似,但是不返回报文主体部分。用于确认uri的有效性以及资源的日期等
DELETE:与put相反,删除uri指定的资源,不安全,一般也不使用
OPTIONS:询问支持方法
TRACE:追踪路径
CONNECT:使用隧道协议连接代理

总结方法:
在这里插入图片描述

响应,状态码及描述

状态码用表示服务器http响应状态的3位数字代码、通过状态码,就可以知道服务端是否正确的处理请求,如果不正确,是什么原因导致的(404)

是http返回给浏览器的状态码

在这里插入图片描述

状态码分类

在这里插入图片描述

常见状态码

2XX 成功:结果正确处理
200 OK:客户端的请求,被正确处理了
204:请求结果正确处理,但响应信息没有正文
206 Partial Content:表示客户端队服务器进行了范围请求,而且服务器成功执行了这部分GET请求,响应报文中由Content-Range指定的实体内容范围

3XX 成功:浏览器需要执行某些特殊的处理以正确处理请求
301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的uri,以后用新的uri,如果之前将老的uri保存为书签了,后面应该按照响应的location首部字段重新保存书签
302 Found:临时重定向,目标资源分配了新的uri,希望用户本次用新的uri访问
在这里插入图片描述
307 Temporary Redirect:临时重定向,该状态码与302有相同含义,307会遵守标准,不会从post变get,每种浏览器可能有不同的情况

4XX 客户端错误:客户端发生错误的原因
400 Bad Request:表明请求报文中存在语法错误,需修改请求内容重新发送,另外,浏览器会像200 OK一样对待该状态码
403 Forbidden:表明浏览器请求的资源被服务器拒绝了,服务器没有必要给出详细理由,如果要说明,可以在响应实体内部说明
404 Not Found:没有请求的资源

5XX 服务器错误:服务器本身发生错误
500 Internal Server Error:服务端执行发生错误,可能是web本身的bug或临时故障
503 Server Unavailable:服务器处于超负荷或正在停机维护,目前无法处理请求,最好写入Retry-After首部字段返回客户端

在这里插入图片描述

CGI机制

CGI(CommonGatewayInterface)是WWW技术中最重要的技术之⼀,有着不可替代的重要地位。CGI是外部应⽤程序(CGI程序)与WEB服务器之间的接⼝标准,是在CGI程序和Web服务器之间传递信息的过程
浏览器除了从服务器下获得资源(⽹⻚,图⽚,⽂字等),有时候还有能上传⼀些东西(提交表单,注册⽤⼾之类的),看看我们⽬前的http只能进⾏获得资源,并不能够进⾏上传资源,所以⽬前http并不具有交互式。为了让我们的⽹站能够实现交互式,我们需要使⽤CGI完成,时刻记着,我们⽬前是要写⼀个http,所以,CGI的所有交互细节,都需要我们来完成。包括http提供的CGI机制和自己实现CGI程序

首先区分get和post的区别
GET⽅法从浏览器传参数给http服务器时,是需要将参数跟到URI后⾯的
POST⽅法从浏览器传参数给http服务器时,是需要将参数放的请求正⽂的

GET⽅法,如果没有传参,http按照⼀般的⽅式进⾏,返回资源即可
GET⽅法,如果有参数传⼊,http就需要按照CGI⽅式处理参数,并将执⾏结果(期望资源)返回给浏览器
POST⽅法,⼀般都需要使⽤CGI⽅式来进⾏处理
在这里插入图片描述

线程池介入

大量链接会导致内部进程线程暴增,让服务器效率恒定

完整代码

服务器部分

TcpServer.hpp

#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include "Log.hpp"

#define BACKLOG 5

class TcpServer
{
   
public:
    TcpServer(int port)
        :_port(port),
         _listen_sock(-1)
    {
   }

    void InitServer()
    {
   
        Socket();
        Bind();
        Listen();
        LOG(INFO, "tcpserver init success...");
    }

    void Socket()
    {
   
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sock < 0)
        {
   
            LOG(FATAL, "socket error!");
            exit(1);
        }

        int opt = 1;
        setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    }

    void Bind()
    {
   
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);

        if (bind(_listen_sock, (const sockaddr*)&local, sizeof(local)) < 0)
        {
   
            LOG(FATAL, "bind error!");
            exit(2);
        }
    }

    void Listen()
    {
   
        if (listen(_listen_sock, BACKLOG) < 0)
        {
   
            LOG(FATAL, "listen error!");
            exit(3);
        }
    }

    static TcpServer* GetInstance(int port)
    {
   
        static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
        if (_svr == nullptr)
        {
   
            pthread_mutex_lock(&mutex);
            if (_svr == nullptr)
            {
   
                _svr = new TcpServer(port);
                _svr->InitServer();
            }
            pthread_mutex_unlock(&mutex);
        }

        return _svr;
    }        

    inline int Sock()
    {
   
        return _listen_sock;
    }

    ~TcpServer()
    {
   
        if(_listen_sock >= 0)
        {
   
            close(_listen_sock);
        }
    }

private:
    int _port;
    int _listen_sock;
    static TcpServer* _svr;

    TcpServer(const TcpServer& x){
   }
};

TcpServer* TcpServer::_svr = nullptr;

HttpServer.hpp

#pragma once
#include <signal.h>
#include "TcpServer.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"

#define PORT 8000

class HttpServer
{
   
public:
    HttpServer(int port = PORT)
        :_port(port),
         //_tpc_server(nullptr),
         _stop(false)
    {
   }

    void InitServer()
    {
   
        // 信号忽略,不然写入时可能直接崩溃
        signal(SIGPIPE, SIG_IGN);
        //_tpc_server = TcpServer::GetInstance(_port);
    }

    void Loop()
    {
   
        //int listen_sock = _tpc_server->Sock();
        TcpServer *tsvr = TcpServer::GetInstance(_port);
        while (!_stop)
        {
   
            struct sockaddr_in peer;
            memset(&peer, 0, sizeof(peer));
            socklen_t len = sizeof(peer);

            int sock = accept(tsvr->Sock(), (sockaddr *)&peer, &len);
            if (sock < 0)
            {
   
                continue;
            }

            LOG(INFO, "get a new link...");
            // int* tmp_sock = new int(sock);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Entrance::HandlerRequest, tmp_sock);

            // 引入线程池
            Task task(sock);
            ThreadPool::GetInstance()->PushTask(task);
            //_pool.push_back(task);
        }
    }

    ~HttpServer()
    {
   }

private:
    int _port;
    //TcpServer* _tpc_server;
    bool _stop;
};

服务器辅助分

Util.hpp

#pragma once
#include <string>
#include <sys/types.h>
#include <sys/socket.h>

class Util
{
   
public:
    static int ReadLine(int sock, std::string& out)
    {
   
        char ch;
        while (ch != '\n')
        {
   
            ssize_t s = recv(sock, &ch, sizeof(ch), 0);
            if (s > 0)
            {
   
                if (ch == '\r')
                {
   
                    // 看看后面是不是\n  \r或\r\n->\n
                    recv(sock, &ch, sizeof(ch), MSG_PEEK);
                    if (ch == '\n')
                    {
   
                        // \r\n只读\n
                        // 窥探到就一定存在
                        recv(sock, &ch, sizeof(ch), 0);
                    }
                    else
                    {
   
                        ch = '\n';
                    }
                }

                // 普通字符
                // \n
                out.push_back(ch);
            }
            else if (s == 0)
            {
   
                return 0;
            }
            else
            {
   
                return -1;
            }
        }

        return out.size();
    }

    static bool CutString(const std::string& target, std::string& sub_out1, std::string& sub_out2, std::string sep)
    {
   
        ssize_t pos = target.find(sep);
        if (pos != std::string::npos)
        {
   
            sub_out1 = target.substr(0, pos);
            sub_out2 = target.substr(pos + sep.size());
            return true;
        }
        return false;
    }

private:
};

线程池部分

#pragma once
#include <queue>
#include <thread>
#include "Task.hpp"

#define NUM 6

class ThreadPool
{
   
private:
    ThreadPool(int num = NUM)
        : _num(num),
          _stop(false)
    {
   
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ThreadPool(const ThreadPool&){
   }

public:
    bool InitThreadPool()
    {
   
        for (int i = 0; i < _num; i++)
        {
   
            pthread_t tid;
            if (pthread_create(&tid, nullptr, ThreadRoutinue, this) != 0)
            {
   
                LOG(FATAL, "create thread pool error");
                return false;
            }
        }

        LOG(FATAL, "create thread pool success");
        return true;
    }

    bool IsStop()
    {
   
        return _stop;
    }

    static void* ThreadRoutinue(void* args)
    {
   
        ThreadPool *tp = (ThreadPool *)(args);
        while (true)
        {
   
            Task task;
            tp->Lock();
            // while 防止误唤醒
            while (tp->TaskQueueIsEmpty())
            {
   
                tp->ThreadWait(); // 醒来时,一定占有锁
            }
            tp->UnLock();
            tp->PopTask(task);
            task.ProcessOn();
        }
    }

    void ThreadWait()
    {
   
        pthread_cond_wait(&_cond, &_mutex);
    }

    void ThreadWakeUp()
    {
   
        pthread_cond_signal(&_cond);
    }

    void Lock()
    {
   
        pthread_mutex_lock(&_mutex);
    }

    void UnLock()
    {
   
        pthread_mutex_unlock(&_mutex);
    }

    void PushTask(const Task& task)
    {
   
        Lock();
        _task_queue.push(task);
        UnLock();
        ThreadWakeUp();
    }

    bool TaskQueueIsEmpty()
    {
   
        return _task_queue.size() == 0 ? true : false;
    }

    void PopTask(Task& task)
    {
   
        task = _task_queue.front();
        _task_queue.pop();
    }

    static ThreadPool* GetInstance()
    {
   
        static pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER;
        if (_instance == nullptr)
        {
   
            pthread_mutex_lock(&_lock);
            if (_instance == nullptr)
            {
   
                _instance = new ThreadPool();
                _instance->InitThreadPool();
            }
            pthread_mutex_unlock(&_lock);
        }

        return _instance;
    }

    ~ThreadPool()
    {
   
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    int _num;
    bool _stop;
    std::queue<Task> _task_queue;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
    static ThreadPool* _instance;
};

ThreadPool* ThreadPool::_instance = nullptr;

Task.hpp

#pragma once
#include "Protocol.hpp"

class Task
{
   
public:
    Task()
    {
   }

    Task(int sock)
        : _sock(sock)
    {
   }

    // 处理任务
    void ProcessOn()
    {
   
        _handler(_sock);
    }

private:
    int _sock;
    CallBack _handler;  // 设置回调
};

协议部分

Protocol.hpp

#pragma once
#include <iostream>
#include <unistd.h>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <algorithm>
#include <sys/wait.h>
#include "Util.hpp"

#define SEP ": "
#define OK 200
#define NOT_FOUND 404
#define BAD_REQUEST 400
#define SERVER_ERROR 500

#define WROOT "wwwroot"
#define HOME_PAGE "index.html"
#define PAGE_404 "404.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

//#define DEBUG

static std::string CodeDesc(int code)
{
   
    std::string desc;
    switch (code)
    {
   
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default: 
        break;
    }

    return desc;
}

static std::string SuffixDesc(const std::string suffix)
{
   
    static std::unordered_map<std::string, std::string> suffixdesc =
        {
   
            {
   ".html", "text/html"},
            {
   ".css", "text/css"},
            {
   ".js", "application/json"},
            {
   ".xml", "application/xml"},
            {
   ".jpg", "image/jpeg"}
        };

    auto it = suffixdesc.find(suffix);
    if (it != suffixdesc.end())
    {
   
        return it->second;
    }

    return "text/html";
}

class HttpRequest
{
   
public:
    HttpRequest()
        :_content_length(0),
        _cgi(false)
    {
   }

    ~HttpRequest()
    {
   }

public:
    std::string _request_line;
    std::vector<std::string> _request_header;
    std::string _blank;
    std::string _request_body;

    // 解析完毕的结果
    std::string _method;
    std::string _url;
    std::string _path;          // 请求目录和参数
    std::string _query_string;
    std::string _version;

    std::unordered_map<std::string, std::string> _header_kv;
    int _content_length;
    std::string _suffix;
    int _size;

    // CGI机制
    bool _cgi;
};

class HttpResponse
{
   
public:
    HttpResponse()
        :_blank(LINE_END),
         _status_code(OK),
         _fd(-1)
    {
   }

    ~HttpResponse()
    {
   }

public:
    std::string _status_line;
    std::vector<std::string> _response_header;
    std::string _blank;
    std::string __response_body;
    int _fd;

    int _status_code;
};

class EndPoint
{
   
public:
    bool RecvHttpRequestLine()
    {
   
        auto &line = _http_request._request_line;
        if (Util::ReadLine(_sock, line) > 0)
        {
   
            line.resize(line.size() - 1);
            LOG(INFO, _http_request._request_line);
        }
        else
        {
   
            _stop = true;
        }

        return _stop;
    }

    bool RecvHttpRequestHeader()
    {
   
        std::string line;
        while (true)
        {
   
            line.clear();
            if (Util::ReadLine(_sock, line) <= 0)
            {
   
                _stop = true;
                break;
            }

            if (line == "\n")
            {
   
                _http_request._blank = line;
                break;
            }

            line.resize(line.size() - 1);
            _http_request._request_header.push_back(line);
        }

        return _stop;
    }

    void ParseHttpRequestLine()
    {
   
        auto& line = _http_request._request_line;
        std::stringstream s(line);
        s >> _http_request._method >> _http_request._url >> _http_request._version;
        auto &method = _http_request._method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
    }

    void ParseHttpRequestHander()
    {
   
        for (auto& line: _http_request._request_header)
        {
   
            std::string key;
            std::string value;
            if (Util::CutString(line, key, value, SEP))
            {
   
                std::cout << key << ":" << value << std::endl;
                _http_request._header_kv.insert({
   key, value});
            }
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
   
        if (_http_request._method == "POST")
        {
      
            auto it = _http_request._header_kv.find("Content-Length");
            if (it != _http_request._header_kv.end())
            {
   
                _http_request._content_length = atoi(it->second.c_str());
                return true;
            }
        }

        return false;
        
    }

    bool RecvHttpRequestBody()
    {
   
        if (IsNeedRecvHttpRequestBody())
        {
   
            int len = _http_request._content_length;
            auto &body = _http_request._request_body;
            char ch = 0;
            while (len)
            {
   
                ssize_t s = recv(_sock, &ch, 1, 0);
                if (s > 0)
                {
   
                    body.push_back(ch);
                    len--;
                }
                else
                {
   
                    _stop = true;
                    break;
                }
            }

        }

        return _stop;
    }

public:
    EndPoint(int sock)
        :_sock(sock),
         _stop(false)
    {
   }

    void RecvHttpRequest()
    {
   
        if (!RecvHttpRequestLine() && !RecvHttpRequestHeader())
        {
   
            ParseHttpRequestLine();
            ParseHttpRequestHander();
            RecvHttpRequestBody();
        }
    }

    // cgi处理
    int ProcessCgi()
    {
   
        int code = OK;
        auto &bin = _http_request._path;
        auto &method = _http_request._method;
        auto &query_string = _http_request._query_string; // GET
        auto &body_text = _http_request._request_body; // POST
        auto &response_body = _http_response.__response_body;
        int content_length = _http_request._content_length;

        // 环境变量,用来GET传
        std::string query_string_env;
        std::string method_env;
        std::string content_length_env;

        // 管道通信
        int input[2];
        int output[2];

        if (pipe(input) < 0)
        {
   
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }

        if (pipe(output) < 0)
        {
   
            LOG(ERROR, "pipe input error");
            code = SERVER_ERROR;
            return code;
        }
        
        // 父进程关闭input写端,只读。关output读端,只写
        // 新线程,从头到尾只有一个进程,不能本进程替换, 创建新进程
        pid_t pid = fork();
        if (pid == 0)
        {
   
            // exec
            close(input[0]);
            close(output[1]);

            // 通过方法判断从哪取参数
            method_env = "METHOD=";
            method_env += method;
            putenv(((char*)method_env.c_str()));
            if (method == "GET")
            {
    
                query_string_env += "QUERY_STRING=";
                query_string_env += query_string;
                // 注册环境变量
                putenv(((char*)query_string_env.c_str()));
            }
            else if (method == "POST")
            {
   
                content_length_env = "CONTENT_LENGTH=";
                content_length_env += std::to_string(content_length);
                putenv((char*)(content_length_env.c_str()));
            }
            else
            {
   
                // do nothing
            }
            std::cout << "替换的程序为:" << bin << std::endl;
            dup2(input[1], 1);
            dup2(output[0], 0);
            execl(bin.c_str(), bin.c_str(), nullptr);
            exit(1);
        }
        else if (pid > 0)
        {
   
            // parent
            close(input[1]);
            close(output[0]);

            if (method == "POST")
            {
   
                const char *start = body_text.c_str();
                int total = 0;
                int size = 0;
                // 防止一次没写完
                while ((total < content_length) && (size = write(output[1], start + total, body_text.size() - total)) > 0)
                {
   
                    total += size;
                }
            }

            // 读取cgi返回的数据
            char ch = 0;
            while (read(input[0], &ch, 1) > 0)
            {
   
                response_body.push_back(ch);
            }

            // 判断子进程退出状态
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if (ret == pid)
            {
   
                if (WIFEXITED(status))
                {
   
                    if (WEXITSTATUS(status) == 0)
                    {
   
                        code = OK;
                    }
                    else
                    {
   
                        code = BAD_REQUEST;
                    }
                }
                else
                {
   
                    code = SERVER_ERROR;
                }
            }

            // 使用完后关闭描述符
            close(input[0]);
            close(output[1]);           
        }
        else
        {
   
            LOG(ERROR, "fork error");
            return 404;
        }

        return code;
    }

    int ProcessNoCgi()
    {
   
        _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
        if (_http_response._fd >= 0)
        {
   
            return OK;
        }
        return NOT_FOUND;
    }

    void BuildOkResponse()
    {
   
        std::string line = "Content-Type: ";
        line += SuffixDesc(_http_request._suffix);
        line += LINE_END;
        _http_response._response_header.push_back(line);

        line += "Content-Length: ";
        if (_http_request._cgi)
        {
   
            line += std::to_string(_http_response.__response_body.size());
        }
        else
        {
   
            // get
            line += std::to_string(_http_request._size);
        }
        line += LINE_END;
        _http_response._response_header.push_back(line);
    }

    void HandlerError(std::string page)
    {
   
        _http_request._cgi = false;
        // 给用户返回对应错误页面
        _http_response._fd = open(page.c_str(), O_RDONLY);
        if (_http_response._fd > 0)
        {
   
            struct stat st;
            stat(page.c_str(), &st);
            _http_request._size = st.st_size;
            std::string line = "Content-Type: text/html";  // 这里明确了是返回网页
            line += LINE_END;
            _http_response._response_header.push_back(line);

            line = "Content-Length: ";
            line += std::to_string(st.st_size);
            line += LINE_END;
            _http_response._response_header.push_back(line);
        }
    }

    void BuildHttpResponseHelper()
    {
   
        auto &code = _http_response._status_code;
        auto &status_line = _http_response._status_line;
        // 构建状态行
        status_line = HTTP_VERSION;
        status_line += " ";
        status_line += std::to_string(code);
        status_line += " ";
        status_line += CodeDesc(code);
        status_line += LINE_END;
        
        // 构建响应正文,可能包含报头
        std::string path = WROOT;
        path += "/";
        switch (code)
        {
   
        case OK:
            BuildOkResponse();
            break;
        case NOT_FOUND:
            path += PAGE_404;
            HandlerError(path);
            break;
        // case 500:
        //     HandlerError(PAGE_500);
        //     break;
        default:
            path += PAGE_404;
            HandlerError(path);
            break;
        }
    }

    void BuildHttpResponse()
    {
   
        struct stat st;
        int found = 0;
        std::string path;
        auto &code = _http_response._status_code;

        std::cout << "方法是: " << _http_request._method << std::endl;
        if (_http_request._method != "GET" && _http_request._method != "POST")
        {
   
            // 非法请求
            LOG(WARNING, "method not is right");
            code = BAD_REQUEST;
            goto END;
        }

        if (_http_request._method == "GET")
        {
   
            ssize_t pos = _http_request._url.find("?");
            if (pos != std::string::npos)
            {
   
                Util::CutString(_http_request._url, _http_request._path, _http_request._query_string, "?");
                _http_request._cgi = true;
            }
            else
            {
   
                _http_request._path = _http_request._url;
            }


        } 
        else if (_http_request._method == "POST")
        {
   
            _http_request._cgi = true;
            _http_request._path = _http_request._url;
        }
        else
        {
   
            // do nothing
        }

        // std::cout << _http_request._path << " " << _http_request._query_string << std::endl;
        path = _http_request._path;
        _http_request._path = WROOT;
        _http_request._path += path;
        // 访问的是根目录, 换成主页
        if (_http_request._path[_http_request._path.size() - 1] == '/')
        {
   
            _http_request._path += HOME_PAGE;
        }

        if (stat(_http_request._path.c_str(), &st) == 0)
        {
   
            // 访问的是目录
            if (S_ISDIR(st.st_mode))
            {
   
                // 不允许访问目录,请求目录,设置为这个目录的主页
                _http_request._path += "/";
                _http_request._path += HOME_PAGE;
                // 上面修改了路径,重新获取
                stat(_http_request._path.c_str(), &st);
            }

            // 访问可执行程序
            if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
   
                std::cout << "访问程序:" << _http_request._path << std::endl;
                // 特殊处理
                _http_request._cgi = true;
            }

            _http_request._size = st.st_size;
        }
        else
        {
   
            // 资源不存在
            LOG(WARNING, _http_request._path + "NOT FOUND");
            code = NOT_FOUND;
            goto END;
        }

        // std::cout << "path:" << _http_request._path << std::endl;
        // suffix
        found = _http_request._path.rfind(".");
        if (found == std::string::npos)
        {
   
            _http_request._suffix = ".html";
        }
        else
        {
   
            _http_request._suffix = _http_request._path.substr(found);
        }

        // 是否cgi处理
        if (_http_request._cgi == true)
        {
   
            code = ProcessCgi();  // 执行目标程序,拿到结果:正文
        }
        else
        {
   
            // 目标网页一定存在
            // 返回要构建http响应
            code = ProcessNoCgi(); // 简单的网页处理,返回静态网页,只需要打开即可 
        }
END:
        // 构建错误响应
        BuildHttpResponseHelper();  // 状态行填充了,报头有了,空行有了,正文有了
    }

    void SendHttpResponse()
    {
   
        //std::cout << "status_line" << _http_response._status_line << std::endl;
        
        // for (auto it : _http_response._response_header)
        // {
   
        //     //std::cout << "status_head" << _http_response._status_line << std::endl;
        // }
        send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
        for (auto it : _http_response._response_header)
        {
   
            send(_sock, it.c_str(), it.size(), 0);
        }
        send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
        //std::string con = "hello world";
        if (_http_request._cgi)
        {
   
            // 内容在response里
            auto &response_body = _http_response.__response_body;
            size_t size = 0;
            size_t total = 0;
            const char *start = response_body.c_str();
            while ((total < response_body.size()) && (size = send(_sock, start + total, response_body.size() - total, 0)) > 0)
            {
   
                total += size;
            }
        }
        else
        {
   
            // 发送打开的文件
            sendfile(_sock, _http_response._fd, nullptr, _http_request._size);
            close(_http_response._fd);
        }
    }

    bool IsStop()
    {
   
        return _stop;
    }

    ~EndPoint()
    {
   }

private:
    int _sock;
    HttpRequest _http_request;
    HttpResponse _http_response;
    bool _stop;
};

class CallBack
{
   
public:
    CallBack()
    {
   }

    void operator()(int sock)
    {
   
        HandlerRequest(sock);
    }

    void HandlerRequest(int sock)
    {
   
        LOG(INFO, "handler request begin...");
        //int sock = *(int*)tmp_sock;
        //delete tmp_sock;

#ifdef DEBUG
        std::cout << "begin---------------------------" << sock << std::endl;
        char buff[4096];
        recv(sock, buff, sizeof(buff), 0);
        std::cout << buff;
        std::cout << "end-----------------------------" << sock << std::endl;
#else
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        if (!ep->IsStop())
        {
   
            LOG(INFO, "recv success, begin build and send");
            ep->BuildHttpResponse();
            ep->SendHttpResponse();
        }
        else
        {
   
            LOG(INFO, "recv error, stop");
        }

        delete ep;
#endif
        LOG(INFO, "handle request end...");
        //return nullptr;
    }
    
    ~CallBack()
    {
   }
};

日志打印

Log.hpp

#pragma 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值