diff --git a/README-docker.md b/README-docker.md index 0c85770..998d7de 100644 --- a/README-docker.md +++ b/README-docker.md @@ -7,7 +7,7 @@ For running this docker, simply use the following commands: # run the container detached, forward internal port 25500 to host port 25500 docker run -d --restart=always -p 25500:25500 tindy2013/subconverter:latest # then check its status -curl http://localhost:25500 +curl http://localhost:25500/version # if you see `subconverter vx.x.x backend` then the container is up and running ``` @@ -37,6 +37,6 @@ docker build -t subconverter-custom:latest . # run the docker detached, forward internal port 25500 to host port 25500 docker run -d --restart=always -p 25500:25500 subconverter-custom:latest # then check its status -curl http://localhost:25500 +curl http://localhost:25500/version # if you see `subconverter vx.x.x backend` then the container is up and running ``` diff --git a/base/pref-new.yml b/base/pref-new.yml index 226d1c6..ea7775e 100644 --- a/base/pref-new.yml +++ b/base/pref-new.yml @@ -126,6 +126,7 @@ aliases: server: listen: 0.0.0.0 port: 25500 + serve_file_root: "" advanced: log_level: info diff --git a/base/pref.ini b/base/pref.ini index 18baf74..bd734c2 100644 --- a/base/pref.ini +++ b/base/pref.ini @@ -251,6 +251,9 @@ listen=0.0.0.0 ;Port to bind on for Web Server port=25500 +;Root folder for web server, keep empty to disable +serve_file_root= + [advanced] log_level=info print_debug_info=false diff --git a/src/interfaces.cpp b/src/interfaces.cpp index 34a4356..cad1939 100644 --- a/src/interfaces.cpp +++ b/src/interfaces.cpp @@ -37,6 +37,9 @@ extern int gLogLevel; extern long gMaxAllowedDownloadSize; string_map gAliases; +extern bool gServeFile; +extern std::string gServeFileRoot; + //global variables for template std::string gTemplatePath = "templates"; string_map gTemplateVars; @@ -837,6 +840,8 @@ void readYAMLConf(YAML::Node &node) { node["server"]["listen"] >> gListenAddress; node["server"]["port"] >> gListenPort; + node["server"]["serve_file_root"] >>= gServeFileRoot; + gServeFile = !gServeFileRoot.empty(); } if(node["advanced"].IsDefined()) @@ -1088,6 +1093,8 @@ void readConf() ini.EnterSection("server"); ini.GetIfExist("listen", gListenAddress); ini.GetIntIfExist("port", gListenPort); + gServeFileRoot = ini.Get("serve_file_root"); + gServeFile = !gServeFileRoot.empty(); ini.EnterSection("advanced"); std::string log_level; diff --git a/src/main.cpp b/src/main.cpp index 66d7748..7790eaa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,10 +143,12 @@ int main(int argc, char *argv[]) if(gGeneratorMode) return simpleGenerator(); + /* append_response("GET", "/", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { return "subconverter " VERSION " backend\n"; }); + */ append_response("GET", "/version", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { diff --git a/src/webserver_libevent.cpp b/src/webserver_libevent.cpp index 209b09f..6c4e6fc 100644 --- a/src/webserver_libevent.cpp +++ b/src/webserver_libevent.cpp @@ -21,6 +21,72 @@ extern std::string user_agent_str; std::atomic_bool SERVER_EXIT_FLAG(false); +// file server +bool gServeFile = false; +std::string gServeFileRoot; + +struct MIME_type +{ + std::string extension; + std::string mimetype; +}; + +MIME_type mime_types[] = {{"html htm shtml","text/html"}, + {"css", "text/css"}, + {"jpeg jpg", "image/jpeg"}, + {"js", "application/javascript"}, + {"txt", "text/plain"}, + {"png", "image/png"}, + {"ico", "image/x-icon"}, + {"svg svgz", "image/svg+xml"}, + {"woff", "application/font-woff"}, + {"json", "application/json"}}; + +bool matchSpaceSeparatedList(const std::string& source, const std::string &target) +{ + string_size pos_begin = 0, pos_end, total = source.size(); + while(pos_begin < total) + { + pos_end = source.find(' ', pos_begin); + if(pos_end == source.npos) + pos_end = total; + if(source.compare(pos_begin, pos_end - pos_begin, target) == 0) + return true; + pos_begin = pos_end + 1; + } + return false; +} + +std::string checkMIMEType(const std::string filename) +{ + string_size name_begin = 0, name_end = 0; + name_begin = filename.rfind('/'); + if(name_begin == filename.npos) + name_begin = 0; + name_end = filename.rfind('.'); + if(name_end == filename.npos || name_end < name_begin || name_end == filename.size() - 1) + return "application/octet-stream"; + std::string extension = filename.substr(name_end + 1); + for(MIME_type &x : mime_types) + if(matchSpaceSeparatedList(x.extension, extension)) + return x.mimetype; + return "application/octet-stream"; +} + +int serveFile(const std::string &filename, std::string &content_type, std::string &return_data) +{ + std::string realname = gServeFileRoot + filename; + if(filename.compare("/") == 0) + realname += "index.html"; + if(!fileExist(realname)) + return 1; + + return_data = fileGet(realname, false); + content_type = checkMIMEType(realname); + writeLog(0, "file-server: serving '" + filename + "' type '" + content_type + "'", LOG_LEVEL_INFO); + return 0; +} + struct responseRoute { std::string method; @@ -83,6 +149,12 @@ static inline int process_request(Request &request, Response &response, std::str return 0; } + if(gServeFile) + { + if(request.method.compare("GET") == 0 && serveFile(request.url, response.content_type, return_data) == 0) + return 0; + } + return -1; }