Util工具类功能设计与类设计(http模块一)

目录

类功能

类定义

类实现

编译测试

Split分割字符串测试

ReadFile读取测试

WriteFile写入测试

UrlEncode编码测试

UrlDecode编码测试

StatuDesc状态码信息获取测试

ExtMime后缀名获取文件mime测试

IsDirectory&IsRegular测试

VaildPath请求路径有效性判断测试

总编译

补充

trunc截断

编码格式的规定

md5sum校验和比较两个文件是否一致

新增gdb调试手法


类功能

类定义

#include "../server.hpp"

class Util{
    public :
        // 字符串分割函数
        size_t Split();
        // 读取文件内容
        static bool ReadFile();
        // 向文件写入数据
        static bool WriteFile();
        // URL编码
        static bool UrlEncode();
        // URL解码
        static bool UrlDecode();
        // 响应状态码的描述信息获取
        static std::string StatuDesc();
        // 根据文件后缀名获取文件mime
        static std::string ExtMime();
        // 判断一个文件是否是一个目录
        static bool IsDirectory();
        // 判断一个文件是否是一个普通文件
        static bool IsRegular();
        // http请求的资源路径有效性判断
        static bool VaildPath();
};

类实现

#include "../server.hpp"
#include <sys/stat.h>
#include <fstream>

class Util
{
public:
    // 字符串分割函数
    static size_t Split(const std::string &src, const std::string &sep, std::vector<std::string> *arry)
    {
        size_t offset = 0;
        // 有10个字符,offset是查找的起始位置,范围应该是0~9,offset==10就代表已经越界了,返回查找的位置
        while (offset < src.size())
        {
            size_t pos = src.find(sep, offset); // 在src字符串偏移量offset处,开始向后查找sep字符/子串,返回查找到的位置
            if (pos == std::string::npos)       // 没有找到特定的字符
            {
                // 将剩余的部分当做一个子串,放入arry中
                if (pos == src.size())
                    break;
                arry->push_back(src.substr(offset));
                return arry->size();
            }
            if (pos == offset)
            {
                offset = pos + sep.size();
                continue; // 当前字符串是一个空串,没有内容
            }
            arry->push_back(src.substr(offset, pos - offset));
            offset = pos + sep.size();
        }
        return arry->size();
    }
    // 读取文件的所有内容,将读取到的内容放到一个容器中
    static bool ReadFile(const std::string &filename, std::string *buf)
    {
        std::ifstream ifs(filename, std::ios::binary); // 以二进制的方式读取
        if (ifs.is_open() == false)
        {
            printf("OPEN %s FILE FAILED!!", filename.c_str());
            return false;
        }
        size_t fsize = 0;            // 偏移量
        ifs.seekg(0, ifs.end);       // 跳转读写位置到末尾
        fsize = ifs.tellg();         // 获取当前读写位置相对于起始位置的偏移量,从末尾偏移量刚好就是文件大小
        ifs.seekg(0, ifs.beg);       // 跳转到起始位置
        buf->resize(fsize);          // 开辟文件大小的空间
        ifs.read(&(*buf)[0], fsize); // c_str()返回的是一个const,所以不行
        if (ifs.good() == false)
        {
            printf("READ %s FILE FAILED!!", filename.c_str());
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }

    // 向文件写入数据
    static bool WriteFile(const std::string &filename, const std::string &buf)
    {
        std::ofstream ofs(filename, std::ios::binary | std::ios::trunc);
        if (ofs.is_open() == false)
        {
            printf("OPEN %s FILE FAILED!!", filename.c_str());
            return false;
        }
        ofs.write(buf.c_str(), buf.size());
        if (ofs.good() == false)
        {
            ERR_LOG("WRITE %s FILE FAILED!", filename.c_str());
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }

    // URL编码,避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产生歧义
    // 编码格式:将特殊字符的ascii值,转换为两个16进制字符,前缀%   C++ -> C%2B%2B
    // 不编码的特殊字符: RFC3986文档规定 . - _ ~ 字母,数字属于绝对不编码字符
    // RFC3986文档规定,编码格式 %HH
    // W3C标准中规定,查询字符串中的空格,需要编码为+, 解码则是+转空格
    static std::string UrlEncode(const std::string url, bool convert_space_to_plus)
    {
        std::string res;
        for (auto &c : url)
        {
            if (c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c))
            {
                res += c;
                continue;
            }
            if (c == ' ' && convert_space_to_plus == true)
            {
                res += '+';
                continue;
            }
            // 剩下的字符都是需要编码成为 %HH 格式
            char tmp[4] = {0};
            // snprintf 与 printf比较类似,都是格式化字符串,只不过一个是打印,一个是放到一块空间中
            snprintf(tmp, 4, "%%%02X", c);
            res += tmp;
        }
        return res;
    }
    static char HEXTOI(char c)
    {
        if (c >= '0' && c <= '9')
            return c - '0';
        else if (c >= 'a' && c <= 'z')
            return c - 'a' + 10; // 注意要加10,16进制中,字母a从11开始
        else if (c >= 'A' && c <= 'Z')
            return c - 'A' + 10;
        return -1;
    }
    // URL解码
    static std::string UrlDecode(const std::string url, bool convert_plus_to_space)
    {
        // 遇到了%,则将紧随其后的2个字符,转换为数字,第一个数字左移4位,然后加上第二个数字
        //  + ->2b  %2b->2 << 4 + 11
        std::string res;
        for (int i = 0; i < url.size(); i++)
        {
            if (url[i] == '+' && convert_plus_to_space == true)
            {
                res += ' ';
                continue;
            }
            if (url[i] == '%')
            {
                char v1 = HEXTOI(url[i + 1]);
                char v2 = HEXTOI(url[i + 2]);
                char v = v1 * 16 + v2;
                i += 2;
                continue;
            }
            res += url[i];
        }
        return res;
    }
    // 响应状态码的描述信息获取
    static std::string StatuDesc(int statu)
    {
        std::unordered_map<int, std::string> _statu_msg = {
            {100, "Continue"},
            {101, "Switching Protocol"},
            {102, "Processing"},
            {103, "Early Hints"},
            {200, "OK"},
            {201, "Created"},
            {202, "Accepted"},
            {203, "Non-Authoritative Information"},
            {204, "No Content"},
            {205, "Reset Content"},
            {206, "Partial Content"},
            {207, "Multi-Status"},
            {208, "Already Reported"},
            {226, "IM Used"},
            {300, "Multiple Choice"},
            {301, "Moved Permanently"},
            {302, "Found"},
            {303, "See Other"},
            {304, "Not Modified"},
            {305, "Use Proxy"},
            {306, "unused"},
            {307, "Temporary Redirect"},
            {308, "Permanent Redirect"},
            {400, "Bad Request"},
            {401, "Unauthorized"},
            {402, "Payment Required"},
            {403, "Forbidden"},
            {404, "Not Found"},
            {405, "Method Not Allowed"},
            {406, "Not Acceptable"},
            {407, "Proxy Authentication Required"},
            {408, "Request Timeout"},
            {409, "Conflict"},
            {410, "Gone"},
            {411, "Length Required"},
            {412, "Precondition Failed"},
            {413, "Payload Too Large"},
            {414, "URI Too Long"},
            {415, "Unsupported Media Type"},
            {416, "Range Not Satisfiable"},
            {417, "Expectation Failed"},
            {418, "I'm a teapot"},
            {421, "Misdirected Request"},
            {422, "Unprocessable Entity"},
            {423, "Locked"},
            {424, "Failed Dependency"},
            {425, "Too Early"},
            {426, "Upgrade Required"},
            {428, "Precondition Required"},
            {429, "Too Many Requests"},
            {431, "Request Header Fields Too Large"},
            {451, "Unavailable For Legal Reasons"},
            {501, "Not Implemented"},
            {502, "Bad Gateway"},
            {503, "Service Unavailable"},
            {504, "Gateway Timeout"},
            {505, "HTTP Version Not Supported"},
            {506, "Variant Also Negotiates"},
            {507, "Insufficient Storage"},
            {508, "Loop Detected"},
            {510, "Not Extended"},
            {511, "Network Authentication Required"}};
        auto it = _statu_msg.find(statu);
        if (it != _statu_msg.end())
        {
            return it->second;
        }
        return "Unknow";
    }
    // 根据文件后缀名获取文件mime
    static std::string ExtMime(const std::string &filename)
    {
        std::unordered_map<std::string, std::string> _mime_msg = {
            {".aac", "audio/aac"},
            {".abw", "application/x-abiword"},
            {".arc", "application/x-freearc"},
            {".avi", "video/x-msvideo"},
            {".azw", "application/vnd.amazon.ebook"},
            {".bin", "application/octet-stream"},
            {".bmp", "image/bmp"},
            {".bz", "application/x-bzip"},
            {".bz2", "application/x-bzip2"},
            {".csh", "application/x-csh"},
            {".css", "text/css"},
            {".csv", "text/csv"},
            {".doc", "application/msword"},
            {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
            {".eot", "application/vnd.ms-fontobject"},
            {".epub", "application/epub+zip"},
            {".gif", "image/gif"},
            {".htm", "text/html"},
            {".html", "text/html"},
            {".ico", "image/vnd.microsoft.icon"},
            {".ics", "text/calendar"},
            {".jar", "application/java-archive"},
            {".jpeg", "image/jpeg"},
            {".jpg", "image/jpeg"},
            {".js", "text/javascript"},
            {".json", "application/json"},
            {".jsonld", "application/ld+json"},
            {".mid", "audio/midi"},
            {".midi", "audio/x-midi"},
            {".mjs", "text/javascript"},
            {".mp3", "audio/mpeg"},
            {".mpeg", "video/mpeg"},
            {".mpkg", "application/vnd.apple.installer+xml"},
            {".odp", "application/vnd.oasis.opendocument.presentation"},
            {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
            {".odt", "application/vnd.oasis.opendocument.text"},
            {".oga", "audio/ogg"},
            {".ogv", "video/ogg"},
            {".ogx", "application/ogg"},
            {".otf", "font/otf"},
            {".png", "image/png"},
            {".pdf", "application/pdf"},
            {".ppt", "application/vnd.ms-powerpoint"},
            {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".rar", "application/x-rar-compressed"},
            {".rtf", "application/rtf"},
            {".sh", "application/x-sh"},
            {".svg", "image/svg+xml"},
            {".swf", "application/x-shockwave-flash"},
            {".tar", "application/x-tar"},
            {".tif", "image/tiff"},
            {".tiff", "image/tiff"},
            {".ttf", "font/ttf"},
            {".txt", "text/plain"},
            {".vsd", "application/vnd.visio"},
            {".wav", "audio/wav"},
            {".weba", "audio/webm"},
            {".webm", "video/webm"},
            {".webp", "image/webp"},
            {".woff", "font/woff"},
            {".woff2", "font/woff2"},
            {".xhtml", "application/xhtml+xml"},
            {".xls", "application/vnd.ms-excel"},
            {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".xml", "application/xml"},
            {".xul", "application/vnd.mozilla.xul+xml"},
            {".zip", "application/zip"},
            {".3gp", "video/3gpp"},
            {".3g2", "video/3gpp2"},
            {".7z", "application/x-7z-compressed"},
        };
        // a.b.txt 获取文件名
        size_t pos = filename.find_last_of('.');
        if (pos != std::string::npos)
            return "application/octet-stream"; // 没找着,表示文件是一个二进制文件
        // 根据扩展名,获取mime
        std::string ext = filename.substr(pos);
        auto it = _mime_msg.find(ext);
        if (it == _mime_msg.end())
            return "application/octet-stream";
        return it->second;
    }
    // 判断一个文件是否是一个目录
    static bool IsDirectory(const std::string &filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if (ret < 0)
            return false;
        return S_ISDIR(st.st_mode);
    }
    // 判断一个文件是否是一个普通文件
    static bool IsRegular(const std::string &filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if (ret < 0)
            return false;
        return S_ISREG(st.st_mode);
    }
    // http请求的资源路径有效性判断
    // /index.html  --- 前边的/叫做相对根目录  映射的是某个服务器上的子目录
    // 想表达的意思就是,客户端只能请求相对根目录中的资源,其他地方的资源都不予理会
    // /../login, 这个路径中的..会让路径的查找跑到相对根目录之外,这是不合理的,不安全的
    static bool VaildPath();
};

编译测试

各个函数依次测试功能

Split分割字符串测试

ReadFile读取测试

WriteFile写入测试

UrlEncode编码测试

UrlDecode编码测试

StatuDesc状态码信息获取测试

ExtMime后缀名获取文件mime测试

IsDirectory&IsRegular测试

VaildPath请求路径有效性判断测试

总编译

无异常,符合预期

测试案例代码

int main()
{
    std::cout << VaildPath("/html/../../index.html") << std::endl;

    /*
    测试(IsDirectory)&(IsRegular)
    std::cout << IsRegular("../regex/regex.cpp") << std::endl;
    std::cout << IsRegular("../regex") << std::endl;
    std::cout << IsDirectory("../regex/regex.cpp") << std::endl;
    std::cout << IsDirectory("../regex") << std::endl;
    */

    /*
    测试(StatuDesc)&(ExtMime)
    std::cout << StatuDesc(200) << std::endl;
    std::cout << ExtMime("a.png") << std::endl;
    */

    /*
    测试(UrlEncode)&(UrlDecode)
    std::string str = "C  ";
    std::string res = UrlEncode(str, false);
    std::string tmp = UrlDecode(res, false);
    std::cout << "[" << res << "]\n";
    std::cout << "[" << tmp << "]\n";
    */

    /*
    测试(UrlEncode)
    std::string str = "/login?user=qingfengyugu&passwwd=C++";
    std::string res = UrlEncode(str, false);
    std::cout << res << std::endl;
    */

    /*
    测试(ReadFile)&(ReadFile)
    std::string buf;
    bool ret = ReadFile("../eventfd/eventfd.c", &buf);
    if (ret == false)
        return false;

    ret = WriteFile("./ttt.c", buf);
    if (ret == false)
        return false;
    */

    /*
    测试(Split)
    std::string str = "abc,,,";
    std::vector<std::string> arry;
    Split(str, ",", &arry);
    for (auto &s : arry)
    {
        std::cout << "[" << s << "]\n";
    }
    */

    return 0;
}

补充

trunc截断

参考文献

c++ 输入输出流  ios::out 和ios::trunc有什么区别-CSDN博客

编码格式的规定

md5sum校验和比较两个文件是否一致

参考文献

Linux下使用md5sum计算和检验MD5码 - 知乎 (zhihu.com)

新增gdb调试手法

建议参考文献

GDB调试run和start的区别_gdb start-CSDN博客

gdb 的 s 和 si 指令有什么区别 - CSDN文库

详解gdb常用指令 | CS笔记 (pynote.net)

相关推荐

  1. 工具util.js)

    2024-03-10 18:04:02       16 阅读
  2. 特殊设计单例模式

    2024-03-10 18:04:02       15 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-10 18:04:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-10 18:04:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-10 18:04:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-10 18:04:02       20 阅读

热门阅读

  1. AI辅助研发,引领科技新潮流

    2024-03-10 18:04:02       27 阅读
  2. C++核心编程

    2024-03-10 18:04:02       18 阅读
  3. 力扣背包问题

    2024-03-10 18:04:02       23 阅读
  4. 【微软技术】介绍

    2024-03-10 18:04:02       24 阅读
  5. 面试题之——SpringBoot的好处?

    2024-03-10 18:04:02       24 阅读
  6. django 的 filter 使用技巧

    2024-03-10 18:04:02       22 阅读