main.cpp
#include <string> #include <valarray> #include <regex> #include <list> #include <thread> #include <cctype> #include <sstream> #include <map> #define VERSION "hais/1.2" #define DEBUG #ifndef DEBUG #define printf #endif #ifdef _MSC_VER #include <WinSock2.h> #include <WS2tcpip.h> #include <tchar.h> #include <strsafe.h> #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "User32.lib") #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable : 4996) #define err(st, msg, ...)\ {\ fprintf(stderr, msg, __VA_ARGS__);\ exit(st);\ } #define close closesocket void DisplayError(const char* msg) { LPVOID lpMsgBuf; const auto dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr); const auto lpDisplayBuf = static_cast<LPVOID>(LocalAlloc( LMEM_ZEROINIT, (lstrlen(static_cast<LPCTSTR>(lpMsgBuf)) + lstrlen(static_cast<LPCTSTR>(msg)) + 40) * sizeof(TCHAR))); StringCchPrintf( static_cast<LPTSTR>(lpDisplayBuf), LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), msg, dw, lpMsgBuf); fprintf(stderr, "%s\n", static_cast<const char*>(lpDisplayBuf)); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); } std::string ToUnixPath(const char* uri) { auto r = std::string("/"); r.append(uri, 1); r.append(uri + 2); for (auto& c : r) { if (c == '\\') c = '/'; } return r; } std::string ToWindowsPath(const char* uri) { auto r = std::string(uri + 1, 1); r.append(":"); r.append(uri + 2); for (auto& c : r) { if (c == '/') c = '\\'; } return r; } #else #include <arpa/inet.h> #include <dirent.h> #include <sys/socket.h> #include <netinet/in.h> #include <err.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <csignal> #endif std::valarray<uint8_t> UrlEncodeTableGenerate() { std::valarray<uint8_t> html5(256); std::generate(begin(html5), end(html5), [&, i = -1]() mutable { ++i; return isalnum(i) || i == '*' || i == '-' || i == '.' || i == '_' ? i : 0; }); return html5; } static auto UrlEncodeTable = UrlEncodeTableGenerate(); int FileExists(const char* path) { #ifdef _MSC_VER const auto attr = GetFileAttributes(path); return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY); #else struct stat sb {}; return !stat(path, &sb) && S_ISREG(sb.st_mode); #endif } int DirectoryExists(const char* path) { #ifdef _MSC_VER const auto attr = GetFileAttributes(path); return attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY; #else struct stat sb {}; return !stat(path, &sb) && S_ISDIR(sb.st_mode); #endif } uint64_t FileSize(const char* path) { #ifdef _MSC_VER WIN32_FILE_ATTRIBUTE_DATA fad; if (!GetFileAttributesEx(path, GetFileExInfoStandard, &fad)) { fprintf(stderr, "%s Can't get file size", path); return 0; } LARGE_INTEGER size; size.HighPart = fad.nFileSizeHigh; size.LowPart = fad.nFileSizeLow; return size.QuadPart; #else struct stat sb {}; stat(path, &sb); return sb.st_size; #endif } std::string FileLastModified(const char* path) { time_t raw; //#ifdef _MSC_VER // #define Oops()\ // {\ // DisplayError("Get last modification time of file");\ // return nullptr;\ // } // auto fh = CreateFile( // path, // GENERIC_READ | FILE_WRITE_ATTRIBUTES, // 0, // NULL, // OPEN_EXISTING, // 0, // NULL); // if (fh == INVALID_HANDLE_VALUE) Oops(); // FILETIME ft; // if (GetFileTime(fh, NULL, NULL, &ft) == 0) Oops(); // ULARGE_INTEGER ull; // ull.LowPart = ft.dwLowDateTime; // ull.HighPart = ft.dwHighDateTime; // raw = ull.QuadPart / 10000000ULL - 11644473600ULL; //#else struct stat sb {}; stat(path, &sb); raw = sb.st_mtime; //#endif char res[35]; strftime(res, 34, "%a, %d %b %G %T GMT", gmtime(&raw)); return res; } std::string UrlEncode(const char* s, const uint16_t len) { #define ToHex(x) ((x) > 9 ? (x) + 55 : (x) + 48) auto res = new char[len * 3]; auto _res = res; const auto end = s + len; for (; s < end; ++s) { const auto t = UrlEncodeTable[static_cast<uint8_t>(*s)]; if (t) { *res++ = t; continue; } *res++ = '%'; *res++ = ToHex(static_cast<uint8_t>(*s) >> 4); *res++ = ToHex(static_cast<uint8_t>(*s) % 16); } *res = 0; auto r = std::string(_res); delete[] _res; return r; } std::string UrlDecode(const char* s, const uint16_t len) { const auto dec = new char[len + 1]; const auto end = s + len; int c; auto o = dec; for (; s < end; o++) { c = *s++; if (c == '+') c = ' '; else if (c == '%') { *s++; *s++; sscanf(s - 2, "%2x", &c); } *o = c; } *o = 0; const auto r = std::string(dec); delete[] dec; return r; } std::string PathCombine(const char* lp, const char* rp) { #ifdef _MSC_VER #define SplitChar "\\" #else #define SplitChar "/" #endif auto path = std::string(lp); if (path[path.length() - 1] != SplitChar[0] && rp[0] != SplitChar[0]) { path.append(SplitChar); } path.append(rp); return path; } void GetFiles(const char* path, std::ostringstream& dirs, std::ostringstream& files) { #define AddFile(oss, href, display, size) \ ((oss) << "<tr><td><a href=\"" << (href) << "\">" << (display) << "</a></td><td align=\"right\">" << (size) << "</td></tr>"); #define AddDir(oss, href, display) \ ((oss) << "<a href=\"" << (href) << "\">" << (display) << "</a><br/>") #ifdef _MSC_VER WIN32_FIND_DATA ffd; LARGE_INTEGER filesize; size_t lengthOfArg; auto hFind = INVALID_HANDLE_VALUE; DWORD dwError = 0; StringCchLength(path, MAX_PATH, &lengthOfArg); if (lengthOfArg > (MAX_PATH - 3)) err(EXIT_FAILURE, "Filename too long"); const auto szDir = PathCombine(path, "*"); hFind = FindFirstFile(szDir.c_str(), &ffd); if (INVALID_HANDLE_VALUE == hFind) DisplayError(szDir.c_str()); do { if (!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { auto dir = PathCombine(ffd.cFileName, ""); auto href = ToUnixPath(PathCombine(path, dir.c_str()).c_str()); AddDir(dirs, UrlEncode(href.c_str(), href.length()), dir); } else { filesize.LowPart = ffd.nFileSizeLow; filesize.HighPart = ffd.nFileSizeHigh; auto href = ToUnixPath(PathCombine(path, ffd.cFileName).c_str()); AddFile( files, UrlEncode(href.c_str(), href.length()), ffd.cFileName, std::to_string(filesize.QuadPart)); } } while (FindNextFile(hFind, &ffd) != 0); dwError = GetLastError(); if (dwError != ERROR_NO_MORE_FILES) DisplayError(szDir.c_str()); FindClose(hFind); #else struct dirent* dent; struct stat st {}; char fn[FILENAME_MAX] = { 0 }; auto len = strlen(path); if (len >= FILENAME_MAX - 1) err(EXIT_FAILURE, "Filename too long"); strcpy(fn, path); if (fn[len - 1] != '/')fn[len++] = '/'; const auto dir = opendir(path); if (!dir) err(EXIT_FAILURE, "Can't open %s", path); while ((dent = readdir(dir))) { if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; strncpy(fn + len, dent->d_name, FILENAME_MAX - len); if (lstat(fn, &st) == -1) { warn("Can't stat %s", fn); continue; } auto _fn = std::string(fn); if (S_ISDIR(st.st_mode)) { auto href = PathCombine(fn, ""); AddDir(dirs, UrlEncode(href.c_str(), href.length()), PathCombine(_fn.substr(len).c_str(), "")); } else { AddFile(files, UrlEncode(_fn.c_str(), _fn.length()), _fn.substr(len), std::to_string(FileSize(_fn.c_str()))); } } if (dir) closedir(dir); #endif } #define GetHttpUrl(url, http, sm)\ std::regex_search(http, sm, std::regex("(POST|GET) .+? HTTP"));\ const auto (url) = std::regex_replace((sm)[0].str(), std::regex("(POST |GET | HTTP|)"), "") static const std::map<std::string, std::string> ContentTypeTable = { {"tif", "image/tiff"}, {"001", "application/x-001"}, {"301", "application/x-301"}, {"323", "text/h323"}, {"906", "application/x-906"}, {"907", "drawing/907"}, {"a11", "application/x-a11"}, {"acp", "audio/x-mei-aac"}, {"ai", "application/postscript"}, {"aif", "audio/aiff"}, {"aifc", "audio/aiff"}, {"aiff", "audio/aiff"}, {"anv", "application/x-anv"}, {"asa", "text/asa"}, {"asf", "video/x-ms-asf"}, {"asp", "text/asp"}, {"asx", "video/x-ms-asf"}, {"au", "audio/basic"}, {"avi", "video/avi"}, {"awf", "application/vnd.adobe.workflow"}, {"biz", "text/xml"}, {"bmp", "application/x-bmp"}, {"bot", "application/x-bot"}, {"c4t", "application/x-c4t"}, {"c90", "application/x-c90"}, {"cal", "application/x-cals"}, {"cat", "application/vnd.ms-pki.seccat"}, {"cdf", "application/x-netcdf"}, {"cdr", "application/x-cdr"}, {"cel", "application/x-cel"}, {"cer", "application/x-x509-ca-cert"}, {"cg4", "application/x-g4"}, {"cgm", "application/x-cgm"}, {"cit", "application/x-cit"}, {"class", "java/*"}, {"cml", "text/xml"}, {"cmp", "application/x-cmp"}, {"cmx", "application/x-cmx"}, {"cot", "application/x-cot"}, {"crl", "application/pkix-crl"}, {"crt", "application/x-x509-ca-cert"}, {"csi", "application/x-csi"}, {"css", "text/css"}, {"cut", "application/x-cut"}, {"dbf", "application/x-dbf"}, {"dbm", "application/x-dbm"}, {"dbx", "application/x-dbx"}, {"dcd", "text/xml"}, {"dcx", "application/x-dcx"}, {"der", "application/x-x509-ca-cert"}, {"dgn", "application/x-dgn"}, {"dib", "application/x-dib"}, {"dll", "application/x-msdownload"}, {"doc", "application/msword"}, {"dot", "application/msword"}, {"drw", "application/x-drw"}, {"dtd", "text/xml"}, {"dwf", "application/x-dwf"}, {"dwg", "application/x-dwg"}, {"dxb", "application/x-dxb"}, {"dxf", "application/x-dxf"}, {"edn", "application/vnd.adobe.edn"}, {"emf", "application/x-emf"}, {"eml", "message/rfc822"}, {"ent", "text/xml"}, {"epi", "application/x-epi"}, {"eps", "application/x-ps"}, {"eps", "application/postscript"}, {"etd", "application/x-ebx"}, {"exe", "application/x-msdownload"}, {"fax", "image/fax"}, {"fdf", "application/vnd.fdf"}, {"fif", "application/fractals"}, {"fo", "text/xml"}, {"frm", "application/x-frm"}, {"g4", "application/x-g4"}, {"gbr", "application/x-gbr"}, {"gif", "image/gif"}, {"gl2", "application/x-gl2"}, {"gp4", "application/x-gp4"}, {"hgl", "application/x-hgl"}, {"hmr", "application/x-hmr"}, {"hpg", "application/x-hpgl"}, {"hpl", "application/x-hpl"}, {"hqx", "application/mac-binhex40"}, {"hrf", "application/x-hrf"}, {"hta", "application/hta"}, {"htc", "text/x-component"}, {"htm", "text/html"}, {"html", "text/html"}, {"htt", "text/webviewhtml"}, {"htx", "text/html"}, {"icb", "application/x-icb"}, {"ico", "image/x-icon"}, {"ico", "application/x-ico"}, {"iff", "application/x-iff"}, {"ig4", "application/x-g4"}, {"igs", "application/x-igs"}, {"iii", "application/x-iphone"}, {"img", "application/x-img"}, {"ins", "application/x-internet-signup"}, {"isp", "application/x-internet-signup"}, {"IVF", "video/x-ivf"}, {"java", "java/*"}, {"jfif", "image/jpeg"}, {"jpe", "image/jpeg"}, {"jpe", "application/x-jpe"}, {"jpeg", "image/jpeg"}, {"jpg", "image/jpeg"}, {"jpg", "application/x-jpg"}, {"js", "application/x-javascript"}, {"jsp", "text/html"}, {"la1", "audio/x-liquid-file"}, {"lar", "application/x-laplayer-reg"}, {"latex", "application/x-latex"}, {"lavs", "audio/x-liquid-secure"}, {"lbm", "application/x-lbm"}, {"lmsff", "audio/x-la-lms"}, {"ls", "application/x-javascript"}, {"ltr", "application/x-ltr"}, {"m1v", "video/x-mpeg"}, {"m2v", "video/x-mpeg"}, {"m3u", "audio/mpegurl"}, {"m4e", "video/mpeg4"}, {"mac", "application/x-mac"}, {"man", "application/x-troff-man"}, {"math", "text/xml"}, {"mdb", "application/x-mdb"}, {"mfp", "application/x-shockwave-flash"}, {"mht", "message/rfc822"}, {"mhtml", "message/rfc822"}, {"mi", "application/x-mi"}, {"mid", "audio/mid"}, {"midi", "audio/mid"}, {"mil", "application/x-mil"}, {"mml", "text/xml"}, {"mnd", "audio/x-musicnet-download"}, {"mns", "audio/x-musicnet-stream"}, {"mocha", "application/x-javascript"}, {"movie", "video/x-sgi-movie"}, {"mp1", "audio/mp1"}, {"mp2", "audio/mp2"}, {"mp2v", "video/mpeg"}, {"mp3", "audio/mp3"}, {"mp4", "video/mp4"}, {"mpa", "video/x-mpg"}, {"mpd", "application/vnd.ms-project"}, {"mpe", "video/x-mpeg"}, {"mpeg", "video/mpg"}, {"mpg", "video/mpg"}, {"mpga", "audio/rn-mpeg"}, {"mpp", "application/vnd.ms-project"}, {"mps", "video/x-mpeg"}, {"mpt", "application/vnd.ms-project"}, {"mpv", "video/mpg"}, {"mpv2", "video/mpeg"}, {"mpw", "application/vnd.ms-project"}, {"mpx", "application/vnd.ms-project"}, {"mtx", "text/xml"}, {"mxp", "application/x-mmxp"}, {"net", "image/pnetvue"}, {"nrf", "application/x-nrf"}, {"nws", "message/rfc822"}, {"odc", "text/x-ms-odc"}, {"out", "application/x-out"}, {"p10", "application/pkcs10"}, {"p12", "application/x-pkcs12"}, {"p7b", "application/x-pkcs7-certificates"}, {"p7c", "application/pkcs7-mime"}, {"p7m", "application/pkcs7-mime"}, {"p7r", "application/x-pkcs7-certreqresp"}, {"p7s", "application/pkcs7-signature"}, {"pc5", "application/x-pc5"}, {"pci", "application/x-pci"}, {"pcl", "application/x-pcl"}, {"pcx", "application/x-pcx"}, {"pdf", "application/pdf"}, {"pdx", "application/vnd.adobe.pdx"}, {"pfx", "application/x-pkcs12"}, {"pgl", "application/x-pgl"}, {"pic", "application/x-pic"}, {"pko", "application/vnd.ms-pki.pko"}, {"pl", "application/x-perl"}, {"plg", "text/html"}, {"pls", "audio/scpls"}, {"plt", "application/x-plt"}, {"png", "image/png"}, {"png", "application/x-png"}, {"pot", "application/vnd.ms-powerpoint"}, {"ppa", "application/vnd.ms-powerpoint"}, {"ppm", "application/x-ppm"}, {"pps", "application/vnd.ms-powerpoint"}, {"ppt", "application/vnd.ms-powerpoint"}, {"ppt", "application/x-ppt"}, {"pr", "application/x-pr"}, {"prf", "application/pics-rules"}, {"prn", "application/x-prn"}, {"prt", "application/x-prt"}, {"ps", "application/x-ps"}, {"ps", "application/postscript"}, {"ptn", "application/x-ptn"}, {"pwz", "application/vnd.ms-powerpoint"}, {"r3t", "text/vnd.rn-realtext3d"}, {"ra", "audio/vnd.rn-realaudio"}, {"ram", "audio/x-pn-realaudio"}, {"ras", "application/x-ras"}, {"rat", "application/rat-file"}, {"rdf", "text/xml"}, {"rec", "application/vnd.rn-recording"}, {"red", "application/x-red"}, {"rgb", "application/x-rgb"}, {"rjs", "application/vnd.rn-realsystem-rjs"}, {"rjt", "application/vnd.rn-realsystem-rjs"}, {"rlc", "application/x-rlc"}, {"rle", "application/x-rle"}, {"rm", "application/vnd.rn-realmedia"}, {"rmf", "application/vnd.adobe.rmf"}, {"rmi", "audio/mid"}, {"rmj", "application/vnd.rn-realsystem-rmj"}, {"rmm", "audio/x-pn-realaudio"}, {"rmp", "application/vnd.rn-rn_music_package"}, {"rms", "application/vnd.rn-realmedia-secure"}, {"rmvb", "application/vnd.rn-realmedia-vbr"}, {"rmx", "application/vnd.rn-realsystem-rmx"}, {"rnx", "application/vnd.rn-realplayer"}, {"rp", "image/vnd.rn-realpix"}, {"rpm", "audio/x-pn-realaudio-plugin"}, {"rsml", "application/vnd.rn-rsml"}, {"rt", "text/vnd.rn-realtext"}, {"rtf", "application/msword"}, {"rtf", "application/x-rtf"}, {"rv", "video/vnd.rn-realvideo"}, {"sam", "application/x-sam"}, {"sat", "application/x-sat"}, {"sdp", "application/sdp"}, {"sdw", "application/x-sdw"}, {"sit", "application/x-stuffit"}, {"slb", "application/x-slb"}, {"sld", "application/x-sld"}, {"slk", "drawing/x-slk"}, {"smi", "application/smil"}, {"smil", "application/smil"}, {"smk", "application/x-smk"}, {"snd", "audio/basic"}, {"sol", "text/plain"}, {"sor", "text/plain"}, {"spc", "application/x-pkcs7-certificates"}, {"spl", "application/futuresplash"}, {"spp", "text/xml"}, {"ssm", "application/streamingmedia"}, {"sst", "application/vnd.ms-pki.certstore"}, {"stl", "application/vnd.ms-pki.stl"}, {"stm", "text/html"}, {"sty", "application/x-sty"}, {"svg", "text/xml"}, {"swf", "application/x-shockwave-flash"}, {"tdf", "application/x-tdf"}, {"tg4", "application/x-tg4"}, {"tga", "application/x-tga"}, {"tif", "image/tiff"}, {"tiff", "image/tiff"}, {"tld", "text/xml"}, {"top", "drawing/x-top"}, {"torrent", "application/x-bittorrent"}, {"tsd", "text/xml"}, {"txt", "text/plain"}, {"uin", "application/x-icq"}, {"uls", "text/iuls"}, {"vcf", "text/x-vcard"}, {"vda", "application/x-vda"}, {"vdx", "application/vnd.visio"}, {"vml", "text/xml"}, {"vpg", "application/x-vpeg005"}, {"vsd", "application/vnd.visio"}, {"vsd", "application/x-vsd"}, {"vss", "application/vnd.visio"}, {"vst", "application/vnd.visio"}, {"vst", "application/x-vst"}, {"vsw", "application/vnd.visio"}, {"vsx", "application/vnd.visio"}, {"vtx", "application/vnd.visio"}, {"vxml", "text/xml"}, {"wav", "audio/wav"}, {"wax", "audio/x-ms-wax"}, {"wb1", "application/x-wb1"}, {"wb2", "application/x-wb2"}, {"wb3", "application/x-wb3"}, {"wbmp", "image/vnd.wap.wbmp"}, {"wiz", "application/msword"}, {"wk3", "application/x-wk3"}, {"wk4", "application/x-wk4"}, {"wkq", "application/x-wkq"}, {"wks", "application/x-wks"}, {"wm", "video/x-ms-wm"}, {"wma", "audio/x-ms-wma"}, {"wmd", "application/x-ms-wmd"}, {"wmf", "application/x-wmf"}, {"wml", "text/vnd.wap.wml"}, {"wmv", "video/x-ms-wmv"}, {"wmx", "video/x-ms-wmx"}, {"wmz", "application/x-ms-wmz"}, {"wp6", "application/x-wp6"}, {"wpd", "application/x-wpd"}, {"wpg", "application/x-wpg"}, {"wpl", "application/vnd.ms-wpl"}, {"wq1", "application/x-wq1"}, {"wr1", "application/x-wr1"}, {"wri", "application/x-wri"}, {"wrk", "application/x-wrk"}, {"ws", "application/x-ws"}, {"ws2", "application/x-ws"}, {"wsc", "text/scriptlet"}, {"wsdl", "text/xml"}, {"wvx", "video/x-ms-wvx"}, {"xdp", "application/vnd.adobe.xdp"}, {"xdr", "text/xml"}, {"xfd", "application/vnd.adobe.xfd"}, {"xfdf", "application/vnd.adobe.xfdf"}, {"xhtml", "text/html"}, {"xls", "application/vnd.ms-excel"}, {"xls", "application/x-xls"}, {"xlw", "application/x-xlw"}, {"xml", "text/xml"}, {"xpl", "audio/scpls"}, {"xq", "text/xml"}, {"xql", "text/xml"}, {"xquery", "text/xml"}, {"xsd", "text/xml"}, {"xsl", "text/xml"}, {"xslt", "text/xml"}, {"xwd", "application/x-xwd"}, {"x_b", "application/x-x_b"}, {"sis", "application/vnd.symbian.install"}, {"sisx", "application/vnd.symbian.install"}, {"x_t", "application/x-x_t"}, {"ipa", "application/vnd.iphone"}, {"apk", "application/vnd.android.package-archive"}, {"xap", "application/x-silverlight-app"} }; std::string GetContentType(const char* path) { const auto len = strlen(path); auto i = path + len - 1; for (; i >= path; --i) { if (*i == '.') break; } const auto ct = ContentTypeTable.find(std::string(i + 1)); if (ct != ContentTypeTable.end()) return ct->second; return "application/octet-stream"; } std::string GetHttpUrlWithoutGet(const char* http, const uint32_t size) { auto start = 0, end = 0; for (auto i = 0; i < size; ++i) { if (http[i] == ' ') { start = i + 1; break; } } for (auto i = start; i < size; ++i) { if (http[i] == ' ' || http[i] == '?') { end = i; break; } } return !start && !end ? std::string() : std::string(http + start, end - start); } void HttpNotFound(const int fd) { static const auto html = "<html><head><title>404 Not Found</title></head>" "<body>" "<center><h1>404 Not Found</h1></center>" "<hr><center>iriszero/" VERSION "</center>" "</body></html>"; static const auto len = strlen(html); std::ostringstream oss; oss << "HTTP/1.1 404 Not Found\r\n" "Content-Length: " << std::to_string(len) << "\r\n" "Content-Type: text/html\r\n" "Server: iriszero/" VERSION "\r\n" "Connection: close\r\n\r\n" << html; const auto http = oss.str(); printf("<========================\n%s\n", http.c_str()); send(fd, http.c_str(), http.length(), 0); } void HttpNotModified(const int fd, const char* lastModified) { std::ostringstream oss; oss << "HTTP/1.1 304 Not Modified\r\n" "Server: iriszero/" VERSION "\r\n" "Last-Modified: " << lastModified << "\r\n" "Connection: close\r\n\r\n"; const auto http = oss.str(); printf("<========================\n%s\n", http.c_str()); send(fd, http.c_str(), http.length(), 0); } void HttpFile( const int fd, const char* path, const char* lastModified, const uint64_t fileSize, const uint64_t offset = 0, uint64_t size = 0) { #define HttpHead(value, http, sm) \ std::regex_search(http, sm, std::regex(""#value": {0,1}.+?\\r{0,1}\\n", std::regex::icase)); \ const auto (value) = std::regex_replace((sm)[0].str(), std::regex("("#value": {0,1}|\\r{0,1}\\n)", std::regex::icase), "") #define IfErrorThenReturn(fun, fp) if((fun) < 0) { fclose(fp); return; } char buf[4096] = {0}; size_t len = 0; const auto fp = fopen(path, "rb"); if (!offset && !size) { std::ostringstream head; head << "HTTP/1.1 200 OK\r\nContent-Length:" << std::to_string(fileSize) << "\r\nConnection: close" "\r\nLast-Modified: " << lastModified << "\r\nContent-Type: " << GetContentType(path) << "\r\nServer: iriszero/" VERSION "\r\n\r\n"; printf("<========================\n%s\n", head.str().c_str()); IfErrorThenReturn(send(fd, head.str().c_str(), head.str().length(), 0), fp); while ((len = fread(buf, sizeof(uint8_t), 4096, fp)) == 4096) IfErrorThenReturn(send(fd, buf, 4096, 0), fp); IfErrorThenReturn(send(fd, buf, len, 0), fp); } else { fseek(fp, offset, SEEK_SET); std::ostringstream head; head << "HTTP/1.1 206 Partial Content\r\nAccept-Ranges: bytes\r\n" << "Server: iriszero/" VERSION "\r\n" << "Content-Type: " << GetContentType(path) << "\r\n" "Content-Length: " << std::to_string(size) << "\r\nContent-Range: bytes " << std::to_string(offset) << "-" << std::to_string(offset + size - 1) << "/" << std::to_string(fileSize) << "\r\nConnection: close\r\n\r\n"; printf("<========================\n%s\n", head.str().c_str()); IfErrorThenReturn(send(fd, head.str().c_str(), head.str().length(), 0), fp); for (; size > 4096; size -= len) { len = fread(buf, sizeof(uint8_t), 4096, fp); IfErrorThenReturn(send(fd, buf, len, 0), fp); } len = fread(buf, sizeof(uint8_t), size, fp); IfErrorThenReturn(send(fd, buf, len, 0), fp); } } void IndexOf(const int fd, const char* path, const char* coding) { std::ostringstream dirs; std::ostringstream files; GetFiles(path, dirs, files); std::ostringstream html; html << " <!DOCTYPE html>" "<html>" << "<head><title>Index of " << path << "</title>" "<meta charset=\"" << coding << "\"/>" "</head>" "<body>" "<h1>Index of " << path << "</h1><hr>" << dirs.str() << (dirs.str().empty() ? "" : "<hr>") << "<table>" << (files.str().empty() ? "" : "<tr><th>File Name</th><th>Size</th></tr>") << files.str() << "</table></body>" "</html>"; std::ostringstream head; head << "HTTP/1.1 200 OK\r\nContent-length: " << std::to_string(html.str().length()) << "\r\nServer: iriszero/" VERSION << "\r\nContent-Type: text/html\r\n\r\n"; printf("<========================\n%s\n", head.str().c_str()); send(fd, head.str().c_str(), head.str().length(), 0); send(fd, html.str().c_str(), html.str().length(), 0); } bool CheckUrl(const std::string& url, const char* path) { std::regex_replace(url, std::regex("\\.\\."), ""); return !memcmp(url.c_str(), path, strlen(path) - 1); } std::list<std::tuple<uint64_t, uint64_t>> GetOffsetAndSize( const std::string& __range, const uint64_t fileSize) { std::list<std::tuple<uint64_t, uint64_t>> res{}; auto _range = __range.substr(6, __range.length() - 6); auto pos = 0; for (auto i = 0; i < _range.length(); ++i) { if (_range[i] == ',') { auto range = _range.substr(pos, i - pos); const int _pos = range.find('-'); auto _start = range.substr(0, _pos); auto _end = range.substr(_pos + 1, range.length() - _pos - 1); if (_start.empty()) { auto end = std::stoull(_end); res.emplace_back(fileSize - end, end); } else if (_end.empty()) { auto start = std::stoull(_start); res.emplace_back(start, fileSize - start); } else { auto start = std::stoull(_start); res.emplace_back(start, std::stoull(_end) - start + 1); } pos = i + 1; } } const auto range = _range.substr(pos, _range.length() - pos); const int _pos = range.find('-'); const auto _start = range.substr(0, _pos); const auto _end = range.substr(_pos + 1, range.length() - _pos - 1); if (_start.empty()) { auto end = std::stoull(_end); res.emplace_back(fileSize - end, end); } else if (_end.empty()) { auto start = std::stoull(_start); res.emplace_back(start, fileSize - start); } else { auto start = std::stoull(_start); res.emplace_back(start, std::stoull(_end) - start + 1); } return res; } void Index(const char* path, const int port, const int threadNum, const char* coding, const char* icoPath) { UrlEncodeTable['/'] = '/'; sockaddr_in svrAddr{}, cliAddr{}; svrAddr.sin_family = AF_INET; svrAddr.sin_addr.s_addr = INADDR_ANY; svrAddr.sin_port = htons(port); char one[4] = {0}; socklen_t sinLen = sizeof(cliAddr); #ifdef _MSC_VER WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) < 0) err(EXIT_FAILURE, "WinSock init fail"); auto sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) err(EXIT_FAILURE, "Can't open socket"); #else struct sigaction action; action.sa_handler = [](int) {}; sigemptyset(&action.sa_mask); action.sa_flags = 0; sigaction(SIGPIPE, &action, nullptr); auto sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock <= 0) err(EXIT_FAILURE, "Can't open socket"); #endif setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, one, sizeof(one)); if (bind(sock, (struct sockaddr *)&svrAddr, sizeof(svrAddr)) < 0) { close(sock); err(1, "Can't bind"); } listen(sock, threadNum); std::valarray<std::thread> pool(threadNum); std::generate(begin(pool), end(pool), [&]() { return std::thread([&]() { const auto iconPath = PathCombine(path, "favicon.ico"); while (true) { const auto clientFd = accept(sock, (struct sockaddr *)&cliAddr, &sinLen); char buf[4096] = {0}; auto len = 0; std::ostringstream _http; while ((len = recv(clientFd, buf, 4096, 0)) == 4096) _http << buf; _http.write(buf, len); auto http = _http.str(); printf( "%s:%d===================>\n%s\n", inet_ntoa(cliAddr.sin_addr), ntohs(cliAddr.sin_port), http.c_str()); std::smatch sm; auto _url = GetHttpUrlWithoutGet(http.c_str(), http.length()); #ifdef _MSC_VER auto url = ToWindowsPath( UrlDecode(_url.c_str(), _url.length()).c_str()); #else auto url = UrlDecode(_url.c_str(), _url.length()); #endif auto urlStatus = false; if (_url.empty()) continue; if (_url == "/") goto index; if (_url == "/favicon.ico" && !FileExists(iconPath.c_str())) { if (!icoPath[0]) HttpNotFound(clientFd); else HttpFile( clientFd, icoPath, FileLastModified(iconPath.c_str()).c_str(), FileSize(iconPath.c_str())); close(clientFd); continue; } urlStatus = CheckUrl(url, path); if (urlStatus && DirectoryExists(url.c_str())) { IndexOf(clientFd, url.c_str(), coding); } else if (urlStatus && FileExists(url.c_str())) { HttpHead(Range, http, sm); if (Range.empty()) { std::regex_search( http, sm, std::regex("If-Modified-Since: {0,1}.+?\\r{0,1}\\n", std::regex::icase)); auto fileLastModified = FileLastModified(url.c_str()); auto lastModified = std::regex_replace( sm[0].str(), std::regex("(If-Modified-Since: {0,1}|\\r{0,1}\\n)", std::regex::icase), ""); if(lastModified == fileLastModified) { HttpNotModified(clientFd, fileLastModified.c_str()); } else { HttpFile( clientFd, url.c_str(), FileLastModified(url.c_str()).c_str(), FileSize(url.c_str())); } } else { for (auto& i : GetOffsetAndSize(Range, FileSize(url.c_str()))) { HttpFile( clientFd, url.c_str(), nullptr, FileSize(url.c_str()), std::get<0>(i), std::get<1>(i)); } } } else { index:; IndexOf(clientFd, path, coding); } close(clientFd); } }); }); for (auto& t : pool) t.join(); } int main(const int argc, char* argv[]) { if (argc == 5) { Index( argv[1], strtol(argv[2], &argv[2], 10), strtol(argv[3], &argv[3], 10), argv[4], ""); } if (argc == 6) { Index( argv[1], strtol(argv[2], &argv[2], 10), strtol(argv[3], &argv[3], 10), argv[4], argv[5]); } err(EXIT_FAILURE, "%s IndexPath Port threadNum Coding [IcoPath]\n", argv[0]); }
CMakeLists.txt
cmake_minimum_required(VERSION 3.1) PROJECT(HttpAutoIndexServer) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS ON) find_package(Threads) AUX_SOURCE_DIRECTORY(src HttpAutoIndexServer) ADD_EXECUTABLE(HttpAutoIndexServer.out main.cpp) TARGET_LINK_LIBRARIES(HttpAutoIndexServer.out pthread)