一、Util模块

二、实现思想
(一)管理
- 实现一些工具接口
- 读取文件内容
- 向文件写入内容
- URL编码
- URL解码
- 通过HTTP状态码获取描述信息
- 通过文件后缀名获取mime
- 判断一个文件是不是目录
- 判断一个文件是否是一个普通文件
- HTTP资源路径有效性判断
(二)意义
在协议支持模块中,当需要某些零碎功能的时候,便于使用!
(三)功能设计
class Util {public:// 字符串分割函数size_t Spilt();// 读取文件内容static bool ReadFile() {}// 向文件写入内容static bool  WriteFile();// URL编码static bool UrlEncode();// URL解码static bool UrlDecode();// 通过HTTP状态码获取描述信息static std::string StatusDesc();// 根据文件后缀名获取文件MINEstatic std::string ExtMine();// 判断一个文件是不是目录static bool IsDirectory();//判断一个文件是否是一个普通文件static bool IsRegular();//HTTP资源路径有效性判断static bool VaildPath();
};
三、代码
class Util {public://字符串分割函数,将src字符串按照sep字符进行分割,得到的各个字串放到arry中,最终返回字串的数量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();}//读取文件的所有内容,将读取的内容放到一个Buffer中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);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;}else if (c >= 'A' && c <= 'Z') {return c - 'A' + 10;}return -1; }static std::string UrlDecode(const std::string url, bool convert_plus_to_space) {//遇到了%,则将紧随其后的2个字符,转换为数字,第一个数字左移4位,然后加上第二个数字  + -> 2b  %2b->2 << 4 + 11std::string res;for (int i = 0; i < url.size(); i++) {if (url[i] == '+' && convert_plus_to_space == true) {res += ' ';continue;}if (url[i] == '%' && (i + 2) < url.size()) {char v1 = HEXTOI(url[i + 1]);char v2 = HEXTOI(url[i + 2]);char v = v1 * 16 + v2;res += v;i += 2;continue;}res += url[i];}return res;}//响应状态码的描述信息获取static std::string StatuDesc(int statu) {auto it = _statu_msg.find(statu);if (it != _statu_msg.end()) {return it->second;}return "Unknow";}//根据文件后缀名获取文件mimestatic std::string ExtMime(const std::string &filename) {// a.b.txt  先获取文件扩展名size_t pos = filename.find_last_of('.');if (pos == std::string::npos) {return "application/octet-stream";}//根据扩展名,获取mimestd::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 ValidPath(const std::string &path) {//思想:按照/进行路径分割,根据有多少子目录,计算目录深度,有多少层,深度不能小于0std::vector<std::string> subdir;Split(path, "/", &subdir);int level = 0;for (auto &dir : subdir) {if (dir == "..") {level--; //任意一层走出相对根目录,就认为有问题if (level < 0) return false;continue;}level++;}return true;}
};
状态码:
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"}
};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"}
};