diff --git a/wpinet/src/main/native/cpp/WebServer.cpp b/wpinet/src/main/native/cpp/WebServer.cpp index e101a2e9200..0e94e6ff043 100644 --- a/wpinet/src/main/native/cpp/WebServer.cpp +++ b/wpinet/src/main/native/cpp/WebServer.cpp @@ -14,11 +14,13 @@ #include #include #include +#include #include #include #include "wpinet/EventLoopRunner.h" #include "wpinet/HttpServerConnection.h" +#include "wpinet/HttpUtil.h" #include "wpinet/UrlParser.h" #include "wpinet/raw_uv_ostream.h" #include "wpinet/uv/GetAddrInfo.h" @@ -39,7 +41,7 @@ class MyHttpConnection : public wpi::HttpServerConnection, protected: void ProcessRequest() override; void SendFileResponse(int code, std::string_view codeText, - std::string_view contentType, std::string_view filename, + std::string_view contentType, fs::path filename, std::string_view extraHeader = {}); std::string m_path; @@ -141,7 +143,7 @@ static std::string_view GetMimeType(std::string_view ext) { void MyHttpConnection::SendFileResponse(int code, std::string_view codeText, std::string_view contentType, - std::string_view filename, + fs::path filename, std::string_view extraHeader) { // open file std::error_code ec; @@ -214,11 +216,51 @@ void MyHttpConnection::ProcessRequest() { query = url.GetQuery(); } // fmt::print(stderr, "query: \"{}\"\n", query); + HttpQueryMap qmap{query}; const bool isGET = m_request.GetMethod() == wpi::HTTP_GET; - if (isGET && wpi::starts_with(path, "/") && !wpi::contains(path, "..")) { - SendFileResponse(200, "OK", GetMimeType(wpi::rsplit(path, '.').second), - fmt::format("{}{}", m_path, path)); + if (isGET && wpi::starts_with(path, '/') && !wpi::contains(path, "..")) { + fs::path fullpath = fmt::format("{}{}", m_path, path); + std::error_code ec; + bool isdir = fs::is_directory(fullpath, ec); + if (isdir) { + if (!wpi::ends_with(path, '/')) { + // redirect to trailing / location + SendResponse(301, "Moved Permanently", "text/plain", "", + fmt::format("Location: {}/\r\n\r\n", path)); + return; + } + // generate directory listing + wpi::SmallString<64> formatBuf; + if (qmap.Get("format", formatBuf).value_or("") == "json") { + wpi::json j = wpi::json::array(); + for (auto&& entry : fs::directory_iterator{fullpath}) { + bool subdir = entry.is_directory(ec); + j.emplace_back( + wpi::json{{"filename", entry.path().filename().string()}, + {"size", subdir ? 0 : entry.file_size(ec)}, + {"directory", subdir}}); + } + SendResponse(200, "OK", "text/json", j.dump()); + } else { + std::string html = fmt::format( + "{}" + "\n", + path); + for (auto&& entry : fs::directory_iterator{fullpath}) { + bool subdir = entry.is_directory(ec); + html += fmt::format( + "", + entry.path().filename().string(), subdir ? "/" : "", + subdir ? "" : fmt::to_string(entry.file_size(ec))); + } + html += "
NameSize
{0}{1}{2}
"; + SendResponse(200, "OK", "text/html", html); + } + } else { + SendFileResponse(200, "OK", GetMimeType(wpi::rsplit(path, '.').second), + fullpath); + } } else { SendError(404, "Resource not found"); }