【高并发服务器:HTTP应用】十五、HttpRequest请求模块 && HttpResponse响应模块设计

文章目录

  • [Ⅰ. `HttpRequest`请求模块设计](#Ⅰ. HttpRequest请求模块设计)
  • [Ⅱ. 接口实现](#Ⅱ. 接口实现)
  • [Ⅲ. `HttpResponse`响应模块设计](#Ⅲ. HttpResponse响应模块设计)
  • [Ⅳ. 接口实现](#Ⅳ. 接口实现)

Ⅰ. HttpRequest请求模块设计

​ 这个模块是 HTTP 请求数据模块,用于存储 HTTP 的请求信息,然后按照 HTTP 请求格式进行解析,得到各个关键要素放到 Request 中,这样子能让 HTTP 请求的分析更加的方便!

其中成员变量,也就是要分析的内容无非就是请求报文中的要素,这些 成员变量我们将其设为公有属性,便于外界的直接访问,如下所示:

  • 请求方法
  • URL
    • 资源路径
    • 查询字符串(用哈希表存储各个键值对,方便查找)
  • 协议版本
  • 头部字段(用哈希表存储各个键值对,方便查找)
  • 正文
  • 此外我们还需要一个 std::smatch 成员变量,用来保存通过使用正则表达式进行解析后得到的数据。(比如资源路径中的数字等等......)

所以需要提供以下接口:

  1. 提供对查询字符串、头部字段的的插入和获取功能
  2. 长连接和短连接的判断
  3. 获取正文长度
cpp 复制代码
class HttpRequest
{
public:
    std::string _method;  									   // 请求方法
    std::string _path;    									   // 资源路径
    std::unordered_map<std::string, std::string> _queryString; // 查询字符串
    std::string _version; 								       // 协议版本
    std::unordered_map<std::string, std::string> _header;      // 头部字段
    std::string _body;    									   // 请求正文

    std::smatch _matches; // 资源路径的正则提取数据
public:
    HttpRequest();

    // 插入头部字段
    void set_header(const std::string& key, const std::string& val);

    // 判断是否存在指定头部字段
    bool has_header(const std::string& key) const;

    // 获取指定头部字段的值
    std::string get_header_val(const std::string& key) const;

    // 插入查询字符串
    void set_queryString(const std::string& key, const std::string& val);

    // 判断是否存在指定查询字符串
    bool has_queryString(const std::string& key) const;

    // 获取指定查询字符串的值
    std::string get_queryString_val(const std::string& key) const;

    // 获取正文长度
    size_t get_body_length() const;

    // 判断是否为短连接
    bool is_short_connection() const;

    // 成员变量清理接口
    void reset();
};

Ⅱ. 接口实现

​ 接口并不难,这里就不细讲了,直接参考注释即可!

cpp 复制代码
class HttpRequest
{
public:
    std::string _method;  									   // 请求方法
    std::string _path;    									   // 资源路径
    std::unordered_map<std::string, std::string> _queryString; // 查询字符串
    std::string _version; 								       // 协议版本
    std::unordered_map<std::string, std::string> _header;      // 头部字段
    std::string _body;    									   // 请求正文

    std::smatch _matches; // 资源路径的正则提取数据
public:
    HttpRequest()
        : _version("HTTP/1.1")
    {}

    // 插入头部字段
    void set_header(const std::string& key, const std::string& val) {  _header[key] = val; }

    // 判断是否存在指定头部字段
    bool has_header(const std::string& key) const
    {
        auto it = _header.find(key);
        if(it == _header.end())
            return false;
        return true;
    }

    // 获取指定头部字段的值
    std::string get_header_val(const std::string& key) const
    {
        auto it = _header.find(key);
        if(it == _header.end())
            return "";
        return it->second;
    }

    // 插入查询字符串
    void set_queryString(const std::string& key, const std::string& val) {  _queryString[key] = val; }

    // 判断是否存在指定查询字符串
    bool has_queryString(const std::string& key) const
    {
        auto it = _queryString.find(key);
        if(it == _queryString.end())
            return false;
        return true;
    }

    // 获取指定查询字符串的值
    std::string get_queryString_val(const std::string& key) const
    {
        auto it = _queryString.find(key);
        if(it == _queryString.end())
            return "";
        return it->second;
    }

    // 获取正文长度
    size_t get_body_length() const 
    {
        // 通过头部字段中的Content-Length来获取,比如Content-Length: 1024\r\n
        bool ret = has_header("Content-Length");
        if(ret == false)
            return 0;
        return std::stol(get_header_val("Content-Length"));
    }

    // 判断是否为短连接
    bool is_short_connection() const
    {
        // 通过头部字段中的Connection来判断,如果是close表示短连接,keep-alive表示长连接
        bool ret = has_header("Connection");
        if(ret == false)
            return false;
        return get_header_val("Connection") == "close";
    }
public:
    // 成员变量清理接口
    void reset()
    {
        _method.clear();
        _path.clear();
        _queryString.clear();
        _version = "HTTP/1.1"; // 注意这里版本号不能清空,因为有地方可能会清理完之后还用到它,为空的话会导致内存错误
        _header.clear();
        _body.clear();
        
        // smatch比较特殊,它没有clear()接口,所以我们可以用一个空的smatch与其进行交换达到清空的效果
        std::smatch tmp;
        _matches.swap(tmp);
    }
};

Ⅲ. HttpResponse响应模块设计

​ 这个模块和上面的请求模块就是相反的,主要存储 HTTP 的响应信息,在进行业务处理的同时,让使用者向 Response 中填充响应要素,完毕后将其组织成为 HTTP 响应格式的数据,发送给客户端,这样子能让 HTTP 响应的过程操作变得简单!

​ 其实响应内容不需要包括响应报文中的全部要素(因为有些是可以通过工具类模块获取的,或者是 http 协议本身自带的),只需要下面几个关键的:

  • 状态码
  • 头部字段
  • 响应正文
  • 重定向信息(是否进行重定向的标志,重定向的路径)

所以需要提供以下接口:

  1. 头部字段的新增、查询和获取
  2. 长短连接的判断与设置
  3. 正文的设置
  4. 重定向的设置
cpp 复制代码
class HttpResponse
{
public:
    int _status;                                          // 状态码
    std::string _body;                                    // 响应正文
    std::unordered_map<std::string, std::string> _header; // 头部字段

    bool _is_redirect;          // 是否重定向的标志
    std::string _redirect_path; // 重定向路径
public:
    HttpResponse();

    // 成员变量清理接口
    void reset();

    // 插入头部字段
    void set_header(const std::string& key, const std::string& val);

    // 判断是否存在指定头部字段
    bool has_header(const std::string& key) const;

    // 获取指定头部字段的值
    std::string get_header_val(const std::string& key) const;

    // 设置响应正文
    void set_content(const std::string& body, const std::string& type = "text/html");

    // 设置重定向信息
    void set_redirect(const std::string& url, int status = 302);

    // 判断是否为短连接
    bool is_short_connection() const;
};

Ⅳ. 接口实现

​ 接口有些甚至和请求模块是一样的,比较简单,这里也不细讲了,具体参考代码!

cpp 复制代码
class HttpResponse
{
public:
    int _status;                                          // 状态码
    std::string _body;                                    // 响应正文
    std::unordered_map<std::string, std::string> _header; // 头部字段

    bool _is_redirect;          // 是否重定向的标志
    std::string _redirect_path; // 重定向路径
public:
    HttpResponse(int status = 200)
        : _is_redirect(false)
        , _status(status)
    {}

    // 成员变量清理接口
    void reset()
    {
        _status = 200;
        _is_redirect = false;
        _body.clear();
        _header.clear();
        _redirect_path.clear();
    }

    // 插入头部字段
    void set_header(const std::string& key, const std::string& val) {  _header[key] = val; }

    // 判断是否存在指定头部字段
    bool has_header(const std::string& key) const 
    {
        auto it = _header.find(key);
        if(it == _header.end())
            return false;
        return true;
    }

    // 获取指定头部字段的值
    std::string get_header_val(const std::string& key) const 
    {
        auto it = _header.find(key);
        if(it == _header.end())
            return "";
        return it->second;
    }

    // 设置响应正文
    void set_content(const std::string& body, const std::string& type = "text/html")
    {
        _body = body;
        set_header("Content-Type", type);
    }

    // 设置重定向信息
    void set_redirect(const std::string& url, int status = 302)
    {
        _status = status;
        _is_redirect = true;
        _redirect_path = url;
    }

    // 判断是否为短连接
    bool is_short_connection() const 
    {
        // 通过头部字段中的Connection来判断,如果是close表示短连接,keep-alive表示长连接
        bool ret = has_header("Connection");
        if(ret == false)
            return 0;
        return get_header_val("Connection") == "close";
    }
};
相关推荐
麦烤楽鸡翅2 小时前
挡住洪水 (牛客)
java·数据结构·c++·python·算法·bfs·牛客
Matana1112 小时前
Vmware中主机ip a没有ip地址
服务器·网络·tcp/ip
征尘bjajmd2 小时前
Java使用okhttp发送get、post请求
java·服务器·数据库
麦烤楽鸡翅2 小时前
【模板】二维前缀和 (牛客)
java·c++·算法·秋招·春招·二维前缀和·面试算法题
guguhaohao3 小时前
map和set,咕咕咕!
数据结构·c++
数字化顾问4 小时前
(114页PPT)华为FusionCloud私有云最佳实践RegionTypeII(附下载方式)
运维·服务器·华为
2501_938810114 小时前
共享IP的定义
服务器·网络·tcp/ip
Larry_Yanan4 小时前
QML学习笔记(五十二)QML与C++交互:数据转换——时间和日期
开发语言·c++·笔记·qt·学习·ui·交互
前端世界4 小时前
用Python打造智能成绩分析系统:从异常处理到断言验证的全流程实战
服务器·数据库·python