From d2e5d4cca642020fdb9adbd9a073a19580b3f22d Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Mon, 29 Jul 2024 14:09:32 -0400 Subject: [PATCH] Implement a function to calculate IANA MIME types Signed-off-by: Juan Cruz Viotti --- src/http/CMakeLists.txt | 4 +- src/http/include/sourcemeta/hydra/http.h | 1 + src/http/include/sourcemeta/hydra/http_mime.h | 30 ++++++++ src/http/mime.cc | 32 +++++++++ test/unit/http/CMakeLists.txt | 3 +- test/unit/http/mime_test.cc | 72 +++++++++++++++++++ 6 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 src/http/include/sourcemeta/hydra/http_mime.h create mode 100644 src/http/mime.cc create mode 100644 test/unit/http/mime_test.cc diff --git a/src/http/CMakeLists.txt b/src/http/CMakeLists.txt index 35138d33..29b7c543 100644 --- a/src/http/CMakeLists.txt +++ b/src/http/CMakeLists.txt @@ -1,7 +1,7 @@ noa_library(NAMESPACE sourcemeta PROJECT hydra NAME http FOLDER "Hydra/HTTP" - PRIVATE_HEADERS method.h status.h error.h header.h time.h - SOURCES method.cc status.cc error.cc header.cc time.cc) + PRIVATE_HEADERS method.h status.h error.h header.h time.h mime.h + SOURCES method.cc status.cc error.cc header.cc time.cc mime.cc) if(HYDRA_INSTALL) noa_library_install(NAMESPACE sourcemeta PROJECT hydra NAME http) diff --git a/src/http/include/sourcemeta/hydra/http.h b/src/http/include/sourcemeta/hydra/http.h index 308901c5..debe1cfa 100644 --- a/src/http/include/sourcemeta/hydra/http.h +++ b/src/http/include/sourcemeta/hydra/http.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/src/http/include/sourcemeta/hydra/http_mime.h b/src/http/include/sourcemeta/hydra/http_mime.h new file mode 100644 index 00000000..ac3233a6 --- /dev/null +++ b/src/http/include/sourcemeta/hydra/http_mime.h @@ -0,0 +1,30 @@ +#ifndef SOURCEMETA_HYDRA_HTTP_MIME_H +#define SOURCEMETA_HYDRA_HTTP_MIME_H + +#if defined(__Unikraft__) +#define SOURCEMETA_HYDRA_HTTP_EXPORT +#else +#include "http_export.h" +#endif + +#include // std::filesystem +#include // std::string + +namespace sourcemeta::hydra { + +/// @ingroup http +/// Find a IANA MIME type for the given file. For example: +/// +/// ```cpp +/// #include +/// #include +/// +/// const auto result{sourcemeta::hydra::mime_type("image.png")}; +/// assert(result == "image/png"); +/// ``` +auto SOURCEMETA_HYDRA_HTTP_EXPORT +mime_type(const std::filesystem::path &file_path) -> std::string; + +} // namespace sourcemeta::hydra + +#endif diff --git a/src/http/mime.cc b/src/http/mime.cc new file mode 100644 index 00000000..381940e0 --- /dev/null +++ b/src/http/mime.cc @@ -0,0 +1,32 @@ +#include + +#include // std::transform +#include // std::tolower +#include // std::map + +namespace sourcemeta::hydra { + +// See https://www.iana.org/assignments/media-types/media-types.xhtml +auto mime_type(const std::filesystem::path &file_path) -> std::string { + // TODO: More exhaustively define all known types + static const std::map MIME_TYPES{ + {".css", "text/css"}, + {".png", "image/png"}, + {".webp", "image/webp"}, + {".ico", "image/vnd.microsoft.icon"}, + {".svg", "image/svg+xml"}, + {".webmanifest", "application/manifest+json"}, + {".json", "application/json"}, + {".woff", "font/woff"}, + {".woff2", "font/woff2"}, + }; + + std::string extension{file_path.extension().string()}; + std::transform(extension.begin(), extension.end(), extension.begin(), + [](const auto character) { return std::tolower(character); }); + const auto result{MIME_TYPES.find(extension)}; + return result == MIME_TYPES.cend() ? "application/octet-stream" + : result->second; +} + +} // namespace sourcemeta::hydra diff --git a/test/unit/http/CMakeLists.txt b/test/unit/http/CMakeLists.txt index e9addc81..d63118e0 100644 --- a/test/unit/http/CMakeLists.txt +++ b/test/unit/http/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(sourcemeta_hydra_http_unit - time_test.cc status_test.cc method_test.cc header_test.cc) + time_test.cc status_test.cc method_test.cc + header_test.cc mime_test.cc) noa_add_default_options(PRIVATE sourcemeta_hydra_http_unit) target_link_libraries(sourcemeta_hydra_http_unit PRIVATE GTest::gtest GTest::gtest_main) diff --git a/test/unit/http/mime_test.cc b/test/unit/http/mime_test.cc new file mode 100644 index 00000000..e52e314e --- /dev/null +++ b/test/unit/http/mime_test.cc @@ -0,0 +1,72 @@ +#include + +#include + +TEST(HTTP_MIME, css_lower) { + const std::filesystem::path file_path{"path/to/example.css"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "text/css"); +} + +TEST(HTTP_MIME, css_upper) { + const std::filesystem::path file_path{"path/to/example.CSS"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "text/css"); +} + +TEST(HTTP_MIME, css_mixed) { + const std::filesystem::path file_path{"path/to/example.cSs"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "text/css"); +} + +TEST(HTTP_MIME, png_lower) { + const std::filesystem::path file_path{"path/to/example.png"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "image/png"); +} + +TEST(HTTP_MIME, webp_lower) { + const std::filesystem::path file_path{"path/to/example.webp"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "image/webp"); +} + +TEST(HTTP_MIME, ico_lower) { + const std::filesystem::path file_path{"path/to/example.ico"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), + "image/vnd.microsoft.icon"); +} + +TEST(HTTP_MIME, svg_lower) { + const std::filesystem::path file_path{"path/to/example.svg"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "image/svg+xml"); +} + +TEST(HTTP_MIME, webmanifest_lower) { + const std::filesystem::path file_path{"path/to/example.webmanifest"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), + "application/manifest+json"); +} + +TEST(HTTP_MIME, json_lower) { + const std::filesystem::path file_path{"path/to/example.json"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "application/json"); +} + +TEST(HTTP_MIME, woff_lower) { + const std::filesystem::path file_path{"path/to/example.woff"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "font/woff"); +} + +TEST(HTTP_MIME, woff2_lower) { + const std::filesystem::path file_path{"path/to/example.woff2"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), "font/woff2"); +} + +TEST(HTTP_MIME, unknown) { + const std::filesystem::path file_path{"path/to/example.foobar"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), + "application/octet-stream"); +} + +TEST(HTTP_MIME, none) { + const std::filesystem::path file_path{"path/to/example"}; + EXPECT_EQ(sourcemeta::hydra::mime_type(file_path), + "application/octet-stream"); +}