1.14.C++ project: Design of Util module that imitates muduo library to implement concurrent server

1. Util module

2. Implement ideas

(1) Management

  1. Implement some tool interfaces
  2. Read file contents
  3. Write content to file
  4. URL encoding
  5. URL decoding
  6. Get description information through HTTP status code
  7. Get mime by file extension
  8. Determine whether a file is a directory
  9. Determine whether a file is an ordinary file
  10. HTTP resource path validity judgment

(2) Meaning

In the protocol support module, it is easy to use when you need some fragmentary functions!

(3) Functional design

class Util {<!-- -->
    public:
        //String splitting function
        size_t Spilt();
        //Read file content
        static bool ReadFile() {<!-- -->

        }
        //Write content to file
        static bool WriteFile();
        //URL encoding
        static bool UrlEncode();
        //URL decoding
        static bool UrlDecode();
        // Get description information through HTTP status code
        static std::string StatusDesc();
        // Get the file MINE based on the file extension name
        static std::string ExtMine();
        // Determine whether a file is a directory
        static bool IsDirectory();
        //Determine whether a file is an ordinary file
        static bool IsRegular();
        //Judge the validity of HTTP resource path
        static bool VaildPath();
};

3. Code

class Util {<!-- -->
    public:
        //String splitting function, split the src string according to sep characters, put each obtained string into arry, and finally return the number of strings
        static size_t Split(const std::string & amp;src, const std::string & amp;sep, std::vector<std::string> *arry) {<!-- -->
            size_t offset = 0;
            // There are 10 characters. Offset is the starting position of the search. The range should be 0~9. Offset==10 means it has crossed the boundary.
            while(offset < src.size()) {<!-- -->
                size_t pos = src.find(sep, offset);//At the src string offset offset, start searching backward for the sep character/string and return to the found position
                if (pos == std::string::npos) {<!-- -->//No specific character found
                    //Treat the remaining part as a string and put it into arry
                    if(pos == src.size()) break;
                    arry->push_back(src.substr(offset));
                    return arry->size();
                }
                if (pos == offset) {<!-- -->
                    offset = pos + sep.size();
                    continue;//The current string is empty and has no content
                }
                arry->push_back(src.substr(offset, pos - offset));
                offset = pos + sep.size();
            }
            return arry->size();
        }
        //Read all the contents of the file and put the read contents into a Buffer
        static bool ReadFile(const std::string & amp;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);//Jump the reading and writing position to the end
            fsize = ifs.tellg(); //Get the offset of the current read and write position relative to the starting position. The offset from the end is exactly the file size
            ifs.seekg(0, ifs.beg);//Jump to the starting position
            buf->resize(fsize); //Open up space of file size
            ifs.read( & amp;(*buf)[0], fsize);
            if (ifs.good() == false) {<!-- -->
                printf("READ %s FILE FAILED!!", filename.c_str());
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }
        //Write data to the file
        static bool WriteFile(const std::string & amp;filename, const std::string & amp;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 encoding to avoid ambiguity between special characters in the resource path and query string in the URL and special characters in the HTTP request
        //Encoding format: Convert the ascii value of special characters into two hexadecimal characters, prefixed % C++ -> C++
        //Unencoded special characters: RFC3986 document stipulates . - _ ~ Letters and numbers are absolutely unencoded characters
        //RFC3986 document stipulates that the encoding format is %HH
        //The W3C standard stipulates that spaces in the query string need to be encoded as + and decoded as + converted to spaces.
        static std::string UrlEncode(const std::string url, bool convert_space_to_plus) {<!-- -->
            std::string res;
            for (auto & amp;c : url) {<!-- -->
                if (c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c)) {<!-- -->
                    res + = c;
                    continue;
                }
                if (c == ' ' & amp; & amp; convert_space_to_plus == true) {<!-- -->
                    res + = ' + ';
                    continue;
                }
                //The remaining characters need to be encoded into %HH format
                char tmp[4] = {<!-- -->0};
                //snprintf is similar to printf. They are both formatted strings, except that one is printed and the other is placed in a space.
                snprintf(tmp, 4, "%% X", c);
                res + = tmp;
            }
            return res;
        }
        static char HEXTOI(char c) {<!-- -->
            if (c >= '0' & amp; & amp; c <= '9') {<!-- -->
                return c - '0';
            }else if (c >= 'a' & amp; & amp; c <= 'z') {<!-- -->
                return c - 'a' + 10;
            }else if (c >= 'A' & amp; & amp; c <= 'Z') {<!-- -->
                return c - 'A' + 10;
            }
            return -1;
        }
        static std::string UrlDecode(const std::string url, bool convert_plus_to_space) {<!-- -->
            //When % is encountered, convert the following 2 characters into numbers, shift the first number to the left by 4 bits, and then add the second number + -> 2b +->2 << 4 + 11
            std::string res;
            for (int i = 0; i < url.size(); i + + ) {<!-- -->
                if (url[i] == ' + ' & amp; & amp; convert_plus_to_space == true) {<!-- -->
                    res + = ' ';
                    continue;
                }
                if (url[i] == '%' & amp; & amp; (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;
        }
        //Get description information of response status code
        static std::string StatuDesc(int statu) {<!-- -->
            
            auto it = _statu_msg.find(statu);
            if (it != _statu_msg.end()) {<!-- -->
                return it->second;
            }
            return "Unknow";
        }
        //Get the file mime based on the file suffix name
        static std::string ExtMime(const std::string & amp;filename) {<!-- -->
            
            // a.b.txt gets the file extension first
            size_t pos = filename.find_last_of('.');
            if (pos == std::string::npos) {<!-- -->
                return "application/octet-stream";
            }
            //According to the extension, get the 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;
        }
        //Determine whether a file is a directory
        static bool IsDirectory(const std::string & amp;filename) {<!-- -->
            struct stat st;
            int ret = stat(filename.c_str(), & amp;st);
            if (ret < 0) {<!-- -->
                return false;
            }
            return S_ISDIR(st.st_mode);
        }
        //Determine whether a file is an ordinary file
        static bool IsRegular(const std::string & amp;filename) {<!-- -->
            struct stat st;
            int ret = stat(filename.c_str(), & amp;st);
            if (ret < 0) {<!-- -->
                return false;
            }
            return S_ISREG(st.st_mode);
        }
        //Judge the validity of the resource path of http request
        // /index.html --- The / in front is called the relative root directory, which maps a subdirectory on a certain server.
        // What I want to express is that the client can only request resources in the relative root directory, and resources in other places will be ignored.
        // /../login, the .. in this path will cause the path search to go outside the relative root directory, which is unreasonable and unsafe.
        static bool ValidPath(const std::string & amp;path) {<!-- -->
            //Idea: Split the path according to /, calculate the directory depth based on how many subdirectories there are, how many layers there are, and the depth cannot be less than 0
            std::vector<std::string> subdir;
            Split(path, "/", & amp;subdir);
            int level = 0;
            for (auto & amp;dir : subdir) {<!-- -->
                if (dir == "..") {<!-- -->
                    level--; //When any level leaves the relative root directory, it is considered a problem.
                    if (level < 0) return false;
                    continue;
                }
                level + + ;
            }
            return true;
        }
};

status code:

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"}
};