diff --git a/include/ylt/thirdparty/cinatra/coro_http_client.hpp b/include/ylt/thirdparty/cinatra/coro_http_client.hpp index f4bfa0cf1..f2331e83d 100644 --- a/include/ylt/thirdparty/cinatra/coro_http_client.hpp +++ b/include/ylt/thirdparty/cinatra/coro_http_client.hpp @@ -144,11 +144,11 @@ class coro_http_client : public std::enable_shared_from_this { }; coro_http_client(asio::io_context::executor_type executor) - : socket_(std::make_shared(executor)), + : executor_wrapper_(executor), + timer_(&executor_wrapper_), + socket_(std::make_shared(executor)), read_buf_(socket_->read_buf_), - chunked_buf_(socket_->chunked_buf_), - executor_wrapper_(executor), - timer_(&executor_wrapper_) {} + chunked_buf_(socket_->chunked_buf_) {} coro_http_client( coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor()) @@ -280,14 +280,14 @@ class coro_http_client : public std::enable_shared_from_this { auto [ok, u] = handle_uri(data, no_schema ? append_uri : uri); if (!ok) { - co_return resp_data{{}, 404}; + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; } auto future = start_timer(req_timeout_duration_, "connect timer"); data = co_await connect(u); if (auto ec = co_await wait_timer(std::move(future)); ec) { - co_return resp_data{{}, 404}; + co_return resp_data{ec, 404}; } if (!data.net_err) { data.status = 200; @@ -644,7 +644,7 @@ class coro_http_client : public std::enable_shared_from_this { } std::error_code err_code; timer_.cancel(err_code); - auto ret = co_await std::move(future); + co_await std::move(future); if (is_timeout_) { co_return std::make_error_code(std::errc::timed_out); } @@ -659,14 +659,15 @@ class coro_http_client : public std::enable_shared_from_this { }); if (form_data_.empty()) { CINATRA_LOG_WARNING << "no multipart"; - co_return resp_data{{}, 404}; + co_return resp_data{std::make_error_code(std::errc::invalid_argument), + 404}; } req_context<> ctx{req_content_type::multipart, "", ""}; resp_data data{}; auto [ok, u] = handle_uri(data, uri); if (!ok) { - co_return resp_data{{}, 404}; + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; } size_t content_len = multipart_content_len(); @@ -678,17 +679,19 @@ class coro_http_client : public std::enable_shared_from_this { std::error_code ec{}; size_t size = 0; - auto future = start_timer(req_timeout_duration_, "connect timer"); + if (socket_->has_closed_) { + auto future = start_timer(req_timeout_duration_, "connect timer"); - data = co_await connect(u); - if (ec = co_await wait_timer(std::move(future)); ec) { - co_return resp_data{{}, 404}; - } - if (data.net_err) { - co_return data; + data = co_await connect(u); + if (ec = co_await wait_timer(std::move(future)); ec) { + co_return resp_data{ec, 404}; + } + if (data.net_err) { + co_return data; + } } - future = start_timer(req_timeout_duration_, "upload timer"); + auto future = start_timer(req_timeout_duration_, "upload timer"); std::tie(ec, size) = co_await async_write(asio::buffer(header_str)); #ifdef INJECT_FOR_HTTP_CLIENT_TEST if (inject_write_failed == ClientInjectAction::write_failed) { @@ -733,7 +736,8 @@ class coro_http_client : public std::enable_shared_from_this { std::string uri, std::string name, std::string filename) { if (!add_file_part(std::move(name), std::move(filename))) { CINATRA_LOG_WARNING << "open file failed or duplicate test names"; - co_return resp_data{{}, 404}; + co_return resp_data{std::make_error_code(std::errc::invalid_argument), + 404}; } co_return co_await async_upload_multipart(std::move(uri)); } @@ -752,6 +756,7 @@ class coro_http_client : public std::enable_shared_from_this { req_context<> ctx{}; if (range.empty()) { + add_header("Transfer-Encoding", "chunked"); ctx = {req_content_type::none, "", "", std::move(file)}; } else { @@ -831,7 +836,7 @@ class coro_http_client : public std::enable_shared_from_this { resp_data data{}; auto [ok, u] = handle_uri(data, uri); if (!ok) { - co_return resp_data{{}, 404}; + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; } constexpr bool is_stream_file = is_stream_ptr_v; @@ -862,17 +867,19 @@ class coro_http_client : public std::enable_shared_from_this { std::error_code ec{}; size_t size = 0; - auto future = start_timer(req_timeout_duration_, "connect timer"); + if (socket_->has_closed_) { + auto future = start_timer(req_timeout_duration_, "connect timer"); - data = co_await connect(u); - if (ec = co_await wait_timer(std::move(future)); ec) { - co_return resp_data{{}, 404}; - } - if (data.net_err) { - co_return data; + data = co_await connect(u); + if (ec = co_await wait_timer(std::move(future)); ec) { + co_return resp_data{ec, 404}; + } + if (data.net_err) { + co_return data; + } } - future = start_timer(req_timeout_duration_, "upload timer"); + auto future = start_timer(req_timeout_duration_, "upload timer"); std::tie(ec, size) = co_await async_write(asio::buffer(header_str)); if (ec) { co_return resp_data{ec, 404}; @@ -895,12 +902,17 @@ class coro_http_client : public std::enable_shared_from_this { } else if constexpr (std::is_same_v || std::is_same_v) { - coro_io::coro_file coro_file(source, coro_io::flags::read_only); - while (!coro_file.eof()) { + coro_io::coro_file file{}; + bool ok = co_await file.async_open(source, coro_io::flags::read_only); + if (!ok) { + co_return resp_data{ + std::make_error_code(std::errc::bad_file_descriptor), 404}; + } + while (!file.eof()) { auto [rd_ec, rd_size] = - co_await coro_file.async_read(file_data.data(), file_data.size()); + co_await file.async_read(file_data.data(), file_data.size()); auto bufs = cinatra::to_chunked_buffers( - file_data.data(), rd_size, chunk_size_str, coro_file.eof()); + file_data.data(), rd_size, chunk_size_str, file.eof()); if (std::tie(ec, size) = co_await async_write(bufs); ec) { co_return resp_data{ec, 404}; } @@ -1321,7 +1333,7 @@ class coro_http_client : public std::enable_shared_from_this { co_return data; } - bool is_ranges = parser_.is_ranges(); + bool is_ranges = parser_.is_resp_ranges(); if (is_ranges) { is_keep_alive = true; } diff --git a/include/ylt/thirdparty/cinatra/coro_http_connection.hpp b/include/ylt/thirdparty/cinatra/coro_http_connection.hpp index 5776cf31e..346aa3b95 100644 --- a/include/ylt/thirdparty/cinatra/coro_http_connection.hpp +++ b/include/ylt/thirdparty/cinatra/coro_http_connection.hpp @@ -88,7 +88,9 @@ class coro_http_connection #endif async_simple::coro::Lazy start() { +#ifdef CINATRA_ENABLE_SSL bool has_shake = false; +#endif while (true) { #ifdef CINATRA_ENABLE_SSL if (use_ssl_ && !has_shake) { @@ -220,6 +222,45 @@ class coro_http_connection co_return true; } + async_simple::coro::Lazy write_data(std::string_view message) { + std::vector buffers; + buffers.push_back(asio::buffer(message)); + auto [ec, _] = co_await async_write(buffers); + if (ec) { + CINATRA_LOG_ERROR << "async_write error: " << ec.message(); + close(); + co_return false; + } + + if (!keep_alive_) { + // now in io thread, so can close socket immediately. + close(); + } + + co_return true; + } + + async_simple::coro::Lazy write_chunked_data(std::string_view buf, + bool eof) { + std::string chunk_size_str = ""; + std::vector buffers = + to_chunked_buffers(buf.data(), buf.length(), + chunk_size_str, eof); + auto [ec, _] = co_await async_write(std::move(buffers)); + if (ec) { + CINATRA_LOG_ERROR << "async_write error: " << ec.message(); + close(); + co_return false; + } + + if (!keep_alive_) { + // now in io thread, so can close socket immediately. + close(); + } + + co_return true; + } + bool sync_reply() { return async_simple::coro::syncAwait(reply()); } async_simple::coro::Lazy begin_chunked() { diff --git a/include/ylt/thirdparty/cinatra/coro_http_request.hpp b/include/ylt/thirdparty/cinatra/coro_http_request.hpp index 3dfafe5e5..112a41121 100644 --- a/include/ylt/thirdparty/cinatra/coro_http_request.hpp +++ b/include/ylt/thirdparty/cinatra/coro_http_request.hpp @@ -54,6 +54,10 @@ class coro_http_request { return is_chunk; } + bool is_resp_ranges() { return parser_.is_resp_ranges(); } + + bool is_req_ranges() { return parser_.is_req_ranges(); } + content_type get_content_type() { static content_type thread_local content_type = get_content_type_impl(); return content_type; diff --git a/include/ylt/thirdparty/cinatra/coro_http_server.hpp b/include/ylt/thirdparty/cinatra/coro_http_server.hpp index 0a71ec288..bd635b893 100644 --- a/include/ylt/thirdparty/cinatra/coro_http_server.hpp +++ b/include/ylt/thirdparty/cinatra/coro_http_server.hpp @@ -10,6 +10,7 @@ #include "async_simple/coro/Lazy.h" #include "cinatra/coro_http_response.hpp" #include "cinatra/coro_http_router.hpp" +#include "cinatra/mime_types.hpp" #include "cinatra_log_wrapper.hpp" #include "coro_http_connection.hpp" #include "ylt/coro_io/coro_io.hpp" @@ -78,7 +79,7 @@ class coro_http_server { promise.setValue(ec); } - return std::move(future); + return future; } // only call once, not thread safe. @@ -150,6 +151,134 @@ class coro_http_server { } } + void set_transfer_chunked_size(size_t size) { chunked_size_ = size; } + + void set_static_res_handler(std::string_view uri_suffix = "", + std::string file_path = "www") { + bool has_double_dot = (file_path.find("..") != std::string::npos) || + (uri_suffix.find("..") != std::string::npos); + if (std::filesystem::path(file_path).has_root_path() || + std::filesystem::path(uri_suffix).has_root_path() || has_double_dot) { + CINATRA_LOG_ERROR << "invalid file path: " << file_path; + std::exit(1); + } + + if (!uri_suffix.empty()) { + static_dir_router_path_ = + std::filesystem::path(uri_suffix).make_preferred().string(); + } + + if (!file_path.empty()) { + static_dir_ = std::filesystem::path(file_path).make_preferred().string(); + } + else { + static_dir_ = fs::absolute(fs::current_path().string()).string(); + } + + files_.clear(); + for (const auto &file : + std::filesystem::recursive_directory_iterator(static_dir_)) { + if (!file.is_directory()) { + files_.push_back(file.path().string()); + } + } + + std::filesystem::path router_path = + std::filesystem::path(static_dir_router_path_); + + std::string uri; + for (auto &file : files_) { + auto relative_path = + std::filesystem::path(file.substr(static_dir_.length())).string(); + if (size_t pos = relative_path.find('\\') != std::string::npos) { + replace_all(relative_path, "\\", "/"); + } + uri = std::string("/") + .append(static_dir_router_path_) + .append(relative_path); + + set_http_handler( + uri, + [this, file_name = file]( + coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + bool is_ranges = req.is_req_ranges(); + + std::string_view extension = get_extension(file_name); + std::string_view mime = get_mime_type(extension); + + std::string content; + detail::resize(content, chunked_size_); + + coro_io::coro_file in_file{}; + co_await in_file.async_open(file_name, coro_io::flags::read_only); + if (!in_file.is_open()) { + resp.set_status_and_content(status_type::not_found, + file_name + "not found"); + co_return; + } + + if (!is_ranges) { + resp.set_format_type(format_type::chunked); + bool ok; + if (ok = co_await resp.get_conn()->begin_chunked(); !ok) { + co_return; + } + + while (true) { + auto [ec, size] = + co_await in_file.async_read(content.data(), content.size()); + if (ec) { + resp.set_status(status_type::no_content); + co_await resp.get_conn()->reply(); + co_return; + } + + bool r = co_await resp.get_conn()->write_chunked( + std::string_view(content.data(), size)); + if (!r) { + co_return; + } + + if (in_file.eof()) { + co_await resp.get_conn()->end_chunked(); + break; + } + } + } + else { + auto range_header = build_range_header( + mime, file_name, coro_io::coro_file::file_size(file_name)); + resp.set_delay(true); + bool r = co_await req.get_conn()->write_data(range_header); + if (!r) { + co_return; + } + + while (true) { + auto [ec, size] = + co_await in_file.async_read(content.data(), content.size()); + if (ec) { + resp.set_status(status_type::no_content); + co_await resp.get_conn()->reply(); + co_return; + } + + r = co_await req.get_conn()->write_data( + std::string_view(content.data(), size)); + if (!r) { + co_return; + } + + if (in_file.eof()) { + break; + } + } + } + }); + } + } + void set_check_duration(auto duration) { check_duration_ = duration; } void set_timeout_duration( @@ -305,6 +434,20 @@ class coro_http_server { } } + std::string build_range_header(std::string_view mime, + std::string_view filename, size_t file_size) { + std::string header_str = + "HTTP/1.1 200 OK\r\nAccess-Control-Allow-origin: " + "*\r\nAccept-Ranges: bytes\r\n"; + header_str.append("Content-Disposition: attachment;filename="); + header_str.append(filename).append("\r\n"); + header_str.append("Connection: keep-alive\r\n"); + header_str.append("Content-Type: ").append(mime).append("\r\n"); + header_str.append("Content-Length: "); + header_str.append(std::to_string(file_size)).append("\r\n\r\n"); + return header_str; + } + private: std::unique_ptr pool_; asio::io_context *out_ctx_ = nullptr; @@ -325,6 +468,11 @@ class coro_http_server { asio::steady_timer check_timer_; bool need_check_ = false; std::atomic stop_timer_ = false; + + std::string static_dir_router_path_ = ""; + std::string static_dir_ = ""; + std::vector files_; + size_t chunked_size_ = 1024 * 10; #ifdef CINATRA_ENABLE_SSL std::string cert_file_; std::string key_file_; diff --git a/include/ylt/thirdparty/cinatra/http_parser.hpp b/include/ylt/thirdparty/cinatra/http_parser.hpp index b2a175e08..55559a431 100644 --- a/include/ylt/thirdparty/cinatra/http_parser.hpp +++ b/include/ylt/thirdparty/cinatra/http_parser.hpp @@ -126,9 +126,14 @@ class http_parser { return false; } - bool is_ranges() const { - auto transfer_encoding = this->get_header_value("Accept-Ranges"sv); - return !transfer_encoding.empty(); + bool is_req_ranges() const { + auto value = this->get_header_value("Range"sv); + return !value.empty(); + } + + bool is_resp_ranges() const { + auto value = this->get_header_value("Accept-Ranges"sv); + return !value.empty(); } bool is_websocket() const { @@ -224,4 +229,4 @@ class http_parser { std::string_view url_; std::unordered_map queries_; }; -} // namespace cinatra +} // namespace cinatra \ No newline at end of file diff --git a/include/ylt/thirdparty/cinatra/mime_types.hpp b/include/ylt/thirdparty/cinatra/mime_types.hpp new file mode 100644 index 000000000..345c60f4c --- /dev/null +++ b/include/ylt/thirdparty/cinatra/mime_types.hpp @@ -0,0 +1,512 @@ +#pragma once +#include +#include + +namespace cinatra { +static const std::map mime_map = { + {".323", "text/h323"}, + {".3gp", "video/3gpp"}, + {".aab", "application/x-authoware-bin"}, + {".aam", "application/x-authoware-map"}, + {".aas", "application/x-authoware-seg"}, + {".acx", "application/internet-property-stream"}, + {".ai", "application/postscript"}, + {".aif", "audio/x-aiff"}, + {".aifc", "audio/x-aiff"}, + {".aiff", "audio/x-aiff"}, + {".als", "audio/X-Alpha5"}, + {".amc", "application/x-mpeg"}, + {".ani", "application/octet-stream"}, + {".apk", "application/vnd.android.package-archive"}, + {".asc", "text/plain"}, + {".asd", "application/astound"}, + {".asf", "video/x-ms-asf"}, + {".asn", "application/astound"}, + {".asp", "application/x-asap"}, + {".asr", "video/x-ms-asf"}, + {".asx", "video/x-ms-asf"}, + {".au", "audio/basic"}, + {".avb", "application/octet-stream"}, + {".avi", "video/x-msvideo"}, + {".awb", "audio/amr-wb"}, + {".axs", "application/olescript"}, + {".bas", "text/plain"}, + {".bcpio", "application/x-bcpio"}, + {".bin ", "application/octet-stream"}, + {".bld", "application/bld"}, + {".bld2", "application/bld2"}, + {".bmp", "image/bmp"}, + {".bpk", "application/octet-stream"}, + {".bz2", "application/x-bzip2"}, + {".c", "text/plain"}, + {".cal", "image/x-cals"}, + {".cat", "application/vnd.ms-pkiseccat"}, + {".ccn", "application/x-cnc"}, + {".cco", "application/x-cocoa"}, + {".cdf", "application/x-cdf"}, + {".cer", "application/x-x509-ca-cert"}, + {".cgi", "magnus-internal/cgi"}, + {".chat", "application/x-chat"}, + {".class", "application/octet-stream"}, + {".clp", "application/x-msclip"}, + {".cmx", "image/x-cmx"}, + {".co", "application/x-cult3d-object"}, + {".cod", "image/cis-cod"}, + {".conf", "text/plain"}, + {".cpio", "application/x-cpio"}, + {".cpp", "text/plain"}, + {".cpt", "application/mac-compactpro"}, + {".crd", "application/x-mscardfile"}, + {".crl", "application/pkix-crl"}, + {".crt", "application/x-x509-ca-cert"}, + {".csh", "application/x-csh"}, + {".csm", "chemical/x-csml"}, + {".csml", "chemical/x-csml"}, + {".css", "text/css"}, + {".cur", "application/octet-stream"}, + {".dcm", "x-lml/x-evm"}, + {".dcr", "application/x-director"}, + {".dcx", "image/x-dcx"}, + {".der", "application/x-x509-ca-cert"}, + {".dhtml", "text/html"}, + {".dir", "application/x-director"}, + {".dll", "application/x-msdownload"}, + {".dmg", "application/octet-stream"}, + {".dms", "application/octet-stream"}, + {".doc", "application/msword"}, + {".docx", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {".dot", "application/msword"}, + {".dvi", "application/x-dvi"}, + {".dwf", "drawing/x-dwf"}, + {".dwg", "application/x-autocad"}, + {".dxf", "application/x-autocad"}, + {".dxr", "application/x-director"}, + {".ebk", "application/x-expandedbook"}, + {".emb", "chemical/x-embl-dl-nucleotide"}, + {".embl", "chemical/x-embl-dl-nucleotide"}, + {".eps", "application/postscript"}, + {".epub", "application/epub+zip"}, + {".eri", "image/x-eri"}, + {".es", "audio/echospeech"}, + {".esl", "audio/echospeech"}, + {".etc", "application/x-earthtime"}, + {".etx", "text/x-setext"}, + {".evm", "x-lml/x-evm"}, + {".evy", "application/envoy"}, + {".exe", "application/octet-stream"}, + {".fh4", "image/x-freehand"}, + {".fh5", "image/x-freehand"}, + {".fhc", "image/x-freehand"}, + {".fif", "application/fractals"}, + {".flr", "x-world/x-vrml"}, + {".flv", "flv-application/octet-stream"}, + {".fm", "application/x-maker"}, + {".fpx", "image/x-fpx"}, + {".fvi", "video/isivideo"}, + {".gau", "chemical/x-gaussian-input"}, + {".gca", "application/x-gca-compressed"}, + {".gdb", "x-lml/x-gdb"}, + {".gif", "image/gif"}, + {".gps", "application/x-gps"}, + {".gtar", "application/x-gtar"}, + {".gz", "application/x-gzip"}, + {".h", "text/plain"}, + {".hdf", "application/x-hdf"}, + {".hdm", "text/x-hdml"}, + {".hdml", "text/x-hdml"}, + {".hlp", "application/winhlp"}, + {".hqx", "application/mac-binhex40"}, + {".hta", "application/hta"}, + {".htc", "text/x-component"}, + {".htm", "text/html"}, + {".html", "text/html"}, + {".hts", "text/html"}, + {".htt", "text/webviewhtml"}, + {".ice", "x-conference/x-cooltalk"}, + {".ico", "image/x-icon"}, + {".ief", "image/ief"}, + {".ifm", "image/gif"}, + {".ifs", "image/ifs"}, + {".iii", "application/x-iphone"}, + {".imy", "audio/melody"}, + {".ins", "application/x-internet-signup"}, + {".ips", "application/x-ipscript"}, + {".ipx", "application/x-ipix"}, + {".isp", "application/x-internet-signup"}, + {".it", "audio/x-mod"}, + {".itz", "audio/x-mod"}, + {".ivr", "i-world/i-vrml"}, + {".j2k", "image/j2k"}, + {".jad", "text/vnd.sun.j2me.app-descriptor"}, + {".jam", "application/x-jam"}, + {".jar", "application/java-archive"}, + {".java", "text/plain"}, + {".jfif", "image/pipeg"}, + {".jnlp", "application/x-java-jnlp-file"}, + {".jpe", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".jpg", "image/jpeg"}, + {".jpz", "image/jpeg"}, + {".js", "application/x-javascript"}, + {".jwc", "application/jwc"}, + {".kjx", "application/x-kjx"}, + {".lak", "x-lml/x-lak"}, + {".latex", "application/x-latex"}, + {".lcc", "application/fastman"}, + {".lcl", "application/x-digitalloca"}, + {".lcr", "application/x-digitalloca"}, + {".lgh", "application/lgh"}, + {".lha", "application/octet-stream"}, + {".lml", "x-lml/x-lml"}, + {".lmlpack", "x-lml/x-lmlpack"}, + {".log", "text/plain"}, + {".lsf", "video/x-la-asf"}, + {".lsx", "video/x-la-asf"}, + {".lzh", "application/octet-stream"}, + {".m13", "application/x-msmediaview"}, + {".m14", "application/x-msmediaview"}, + {".m15", "audio/x-mod"}, + {".m3u", "audio/x-mpegurl"}, + {".m3url", "audio/x-mpegurl"}, + {".m4a", "audio/mp4a-latm"}, + {".m4b", "audio/mp4a-latm"}, + {".m4p", "audio/mp4a-latm"}, + {".m4u", "video/vnd.mpegurl"}, + {".m4v", "video/x-m4v"}, + {".ma1", "audio/ma1"}, + {".ma2", "audio/ma2"}, + {".ma3", "audio/ma3"}, + {".ma5", "audio/ma5"}, + {".man", "application/x-troff-man"}, + {".map", "magnus-internal/imagemap"}, + {".mbd", "application/mbedlet"}, + {".mct", "application/x-mascot"}, + {".mdb", "application/x-msaccess"}, + {".mdz", "audio/x-mod"}, + {".me", "application/x-troff-me"}, + {".mel", "text/x-vmel"}, + {".mht", "message/rfc822"}, + {".mhtml", "message/rfc822"}, + {".mi", "application/x-mif"}, + {".mid", "audio/mid"}, + {".midi", "audio/midi"}, + {".mif", "application/x-mif"}, + {".mil", "image/x-cals"}, + {".mio", "audio/x-mio"}, + {".mmf", "application/x-skt-lbs"}, + {".mng", "video/x-mng"}, + {".mny", "application/x-msmoney"}, + {".moc", "application/x-mocha"}, + {".mocha", "application/x-mocha"}, + {".mod", "audio/x-mod"}, + {".mof", "application/x-yumekara"}, + {".mol", "chemical/x-mdl-molfile"}, + {".mop", "chemical/x-mopac-input"}, + {".mov", "video/quicktime"}, + {".movie", "video/x-sgi-movie"}, + {".mp2", "video/mpeg"}, + {".mp3", "audio/mpeg"}, + {".mp4", "video/mp4"}, + {".mpa", "video/mpeg"}, + {".mpc", "application/vnd.mpohun.certificate"}, + {".mpe", "video/mpeg"}, + {".mpeg", "video/mpeg"}, + {".mpg", "video/mpeg"}, + {".mpg4", "video/mp4"}, + {".mpga", "audio/mpeg"}, + {".mpn", "application/vnd.mophun.application"}, + {".mpp", "application/vnd.ms-project"}, + {".mps", "application/x-mapserver"}, + {".mpv2", "video/mpeg"}, + {".mrl", "text/x-mrml"}, + {".mrm", "application/x-mrm"}, + {".ms", "application/x-troff-ms"}, + {".msg", "application/vnd.ms-outlook"}, + {".mts", "application/metastream"}, + {".mtx", "application/metastream"}, + {".mtz", "application/metastream"}, + {".mvb", "application/x-msmediaview"}, + {".mzv", "application/metastream"}, + {".nar", "application/zip"}, + {".nbmp", "image/nbmp"}, + {".nc", "application/x-netcdf"}, + {".ndb", "x-lml/x-ndb"}, + {".ndwn", "application/ndwn"}, + {".nif", "application/x-nif"}, + {".nmz", "application/x-scream"}, + {".nokia-op-logo", "image/vnd.nok-oplogo-color"}, + {".npx", "application/x-netfpx"}, + {".nsnd", "audio/nsnd"}, + {".nva", "application/x-neva1"}, + {".nws", "message/rfc822"}, + {".oda", "application/oda"}, + {".ogg", "audio/ogg"}, + {".oom", "application/x-AtlasMate-Plugin"}, + {".p10", "application/pkcs10"}, + {".p12", "application/x-pkcs12"}, + {".p7b", "application/x-pkcs7-certificates"}, + {".p7c", "application/x-pkcs7-mime"}, + {".p7m", "application/x-pkcs7-mime"}, + {".p7r", "application/x-pkcs7-certreqresp"}, + {".p7s", "application/x-pkcs7-signature"}, + {".pac", "audio/x-pac"}, + {".pae", "audio/x-epac"}, + {".pan", "application/x-pan"}, + {".pbm", "image/x-portable-bitmap"}, + {".pcx", "image/x-pcx"}, + {".pda", "image/x-pda"}, + {".pdb", "chemical/x-pdb"}, + {".pdf", "application/pdf"}, + {".pfr", "application/font-tdpfr"}, + {".pfx", "application/x-pkcs12"}, + {".pgm", "image/x-portable-graymap"}, + {".pict", "image/x-pict"}, + {".pko", "application/ynd.ms-pkipko"}, + {".pm", "application/x-perl"}, + {".pma", "application/x-perfmon"}, + {".pmc", "application/x-perfmon"}, + {".pmd", "application/x-pmd"}, + {".pml", "application/x-perfmon"}, + {".pmr", "application/x-perfmon"}, + {".pmw", "application/x-perfmon"}, + {".png", "image/png"}, + {".pnm", "image/x-portable-anymap"}, + {".pnz", "image/png"}, + {".pot,", "application/vnd.ms-powerpoint"}, + {".ppm", "image/x-portable-pixmap"}, + {".pps", "application/vnd.ms-powerpoint"}, + {".ppt", "application/vnd.ms-powerpoint"}, + {".pptx", + "application/" + "vnd.openxmlformats-officedocument.presentationml.presentation"}, + {".pqf", "application/x-cprplayer"}, + {".pqi", "application/cprplayer"}, + {".prc", "application/x-prc"}, + {".prf", "application/pics-rules"}, + {".prop", "text/plain"}, + {".proxy", "application/x-ns-proxy-autoconfig"}, + {".ps", "application/postscript"}, + {".ptlk", "application/listenup"}, + {".pub", "application/x-mspublisher"}, + {".pvx", "video/x-pv-pvx"}, + {".qcp", "audio/vnd.qcelp"}, + {".qt", "video/quicktime"}, + {".qti", "image/x-quicktime"}, + {".qtif", "image/x-quicktime"}, + {".r3t", "text/vnd.rn-realtext3d"}, + {".ra", "audio/x-pn-realaudio"}, + {".ram", "audio/x-pn-realaudio"}, + {".rar", "application/octet-stream"}, + {".ras", "image/x-cmu-raster"}, + {".rc", "text/plain"}, + {".rdf", "application/rdf+xml"}, + {".rf", "image/vnd.rn-realflash"}, + {".rgb", "image/x-rgb"}, + {".rlf", "application/x-richlink"}, + {".rm", "audio/x-pn-realaudio"}, + {".rmf", "audio/x-rmf"}, + {".rmi", "audio/mid"}, + {".rmm", "audio/x-pn-realaudio"}, + {".rmvb", "audio/x-pn-realaudio"}, + {".rnx", "application/vnd.rn-realplayer"}, + {".roff", "application/x-troff"}, + {".rp", "image/vnd.rn-realpix"}, + {".rpm", "audio/x-pn-realaudio-plugin"}, + {".rt", "text/vnd.rn-realtext"}, + {".rte", "x-lml/x-gps"}, + {".rtf", "application/rtf"}, + {".rtg", "application/metastream"}, + {".rtx", "text/richtext"}, + {".rv", "video/vnd.rn-realvideo"}, + {".rwc", "application/x-rogerwilco"}, + {".s3m", "audio/x-mod"}, + {".s3z", "audio/x-mod"}, + {".sca", "application/x-supercard"}, + {".scd", "application/x-msschedule"}, + {".sct", "text/scriptlet"}, + {".sdf", "application/e-score"}, + {".sea", "application/x-stuffit"}, + {".setpay", "application/set-payment-initiation"}, + {".setreg", "application/set-registration-initiation"}, + {".sgm", "text/x-sgml"}, + {".sgml", "text/x-sgml"}, + {".sh", "application/x-sh"}, + {".shar", "application/x-shar"}, + {".shtml", "magnus-internal/parsed-html"}, + {".shw", "application/presentations"}, + {".si6", "image/si6"}, + {".si7", "image/vnd.stiwap.sis"}, + {".si9", "image/vnd.lgtwap.sis"}, + {".sis", "application/vnd.symbian.install"}, + {".sit", "application/x-stuffit"}, + {".skd", "application/x-Koan"}, + {".skm", "application/x-Koan"}, + {".skp", "application/x-Koan"}, + {".skt", "application/x-Koan"}, + {".slc", "application/x-salsa"}, + {".smd", "audio/x-smd"}, + {".smi", "application/smil"}, + {".smil", "application/smil"}, + {".smp", "application/studiom"}, + {".smz", "audio/x-smd"}, + {".snd", "audio/basic"}, + {".spc", "application/x-pkcs7-certificates"}, + {".spl", "application/futuresplash"}, + {".spr", "application/x-sprite"}, + {".sprite", "application/x-sprite"}, + {".sdp", "application/sdp"}, + {".spt", "application/x-spt"}, + {".src", "application/x-wais-source"}, + {".sst", "application/vnd.ms-pkicertstore"}, + {".stk", "application/hyperstudio"}, + {".stl", "application/vnd.ms-pkistl"}, + {".stm", "text/html"}, + {".svg", "image/svg+xml"}, + {".sv4cpio", "application/x-sv4cpio"}, + {".sv4crc", "application/x-sv4crc"}, + {".svf", "image/vnd"}, + {".eot", "application/vnd.ms-fontobject"}, + {".woff", "application/font-woff"}, + {".svh", "image/svh"}, + {".svr", "x-world/x-svr"}, + {".swf", "application/x-shockwave-flash"}, + {".swfl", "application/x-shockwave-flash"}, + {".t", "application/x-troff"}, + {".tad", "application/octet-stream"}, + {".talk", "text/x-speech"}, + {".tar", "application/x-tar"}, + {".taz", "application/x-tar"}, + {".tbp", "application/x-timbuktu"}, + {".tbt", "application/x-timbuktu"}, + {".tcl", "application/x-tcl"}, + {".tex", "application/x-tex"}, + {".texi", "application/x-texinfo"}, + {".texinfo", "application/x-texinfo"}, + {".tgz", "application/x-compressed"}, + {".thm", "application/vnd.eri.thm"}, + {".tif", "image/tiff"}, + {".tiff", "image/tiff"}, + {".tki", "application/x-tkined"}, + {".tkined", "application/x-tkined"}, + {".toc", "application/toc"}, + {".toy", "image/toy"}, + {".tr", "application/x-troff"}, + {".trk", "x-lml/x-gps"}, + {".trm", "application/x-msterminal"}, + {".tsi", "audio/tsplayer"}, + {".tsp", "application/dsptype"}, + {".tsv", "text/tab-separated-values"}, + {".ttf", "application/octet-stream"}, + {".ttz", "application/t-time"}, + {".txt", "text/plain"}, + {".uls", "text/iuls"}, + {".ult", "audio/x-mod"}, + {".ustar", "application/x-ustar"}, + {".uu", "application/x-uuencode"}, + {".uue", "application/x-uuencode"}, + {".vcd", "application/x-cdlink"}, + {".vcf", "text/x-vcard"}, + {".vdo", "video/vdo"}, + {".vib", "audio/vib"}, + {".viv", "video/vivo"}, + {".vivo", "video/vivo"}, + {".vmd", "application/vocaltec-media-desc"}, + {".vmf", "application/vocaltec-media-file"}, + {".vmi", "application/x-dreamcast-vms-info"}, + {".vms", "application/x-dreamcast-vms"}, + {".vox", "audio/voxware"}, + {".vqe", "audio/x-twinvq-plugin"}, + {".vqf", "audio/x-twinvq"}, + {".vql", "audio/x-twinvq"}, + {".vre", "x-world/x-vream"}, + {".vrml", "x-world/x-vrml"}, + {".vrt", "x-world/x-vrt"}, + {".vrw", "x-world/x-vream"}, + {".vts", "workbook/formulaone"}, + {".wav", "audio/x-wav"}, + {".wax", "audio/x-ms-wax"}, + {".wbmp", "image/vnd.wap.wbmp"}, + {".wcm", "application/vnd.ms-works"}, + {".wdb", "application/vnd.ms-works"}, + {".web", "application/vnd.xara"}, + {".wi", "image/wavelet"}, + {".wis", "application/x-InstallShield"}, + {".wks", "application/vnd.ms-works"}, + {".wm", "video/x-ms-wm"}, + {".wma", "audio/x-ms-wma"}, + {".wmd", "application/x-ms-wmd"}, + {".wmf", "application/x-msmetafile"}, + {".wml", "text/vnd.wap.wml"}, + {".wmlc", "application/vnd.wap.wmlc"}, + {".wmls", "text/vnd.wap.wmlscript"}, + {".wmlsc", "application/vnd.wap.wmlscriptc"}, + {".wmlscript", "text/vnd.wap.wmlscript"}, + {".wmv", "audio/x-ms-wmv"}, + {".wmx", "video/x-ms-wmx"}, + {".wmz", "application/x-ms-wmz"}, + {".wpng", "image/x-up-wpng"}, + {".wps", "application/vnd.ms-works"}, + {".wpt", "x-lml/x-gps"}, + {".wri", "application/x-mswrite"}, + {".wrl", "x-world/x-vrml"}, + {".wrz", "x-world/x-vrml"}, + {".ws", "text/vnd.wap.wmlscript"}, + {".wsc", "application/vnd.wap.wmlscriptc"}, + {".wv", "video/wavelet"}, + {".wvx", "video/x-ms-wvx"}, + {".wxl", "application/x-wxl"}, + {".x-gzip", "application/x-gzip"}, + {".xaf", "x-world/x-vrml"}, + {".xar", "application/vnd.xara"}, + {".xbm", "image/x-xbitmap"}, + {".xdm", "application/x-xdma"}, + {".xdma", "application/x-xdma"}, + {".xdw", "application/vnd.fujixerox.docuworks"}, + {".xht", "application/xhtml+xml"}, + {".xhtm", "application/xhtml+xml"}, + {".xhtml", "application/xhtml+xml"}, + {".xla", "application/vnd.ms-excel"}, + {".xlc", "application/vnd.ms-excel"}, + {".xll", "application/x-excel"}, + {".xlm", "application/vnd.ms-excel"}, + {".xls", "application/vnd.ms-excel"}, + {".xlsx", + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {".xlt", "application/vnd.ms-excel"}, + {".xlw", "application/vnd.ms-excel"}, + {".xm", "audio/x-mod"}, + {".xml", "text/plain"}, + {".xml", "application/xml"}, + {".xmz", "audio/x-mod"}, + {".xof", "x-world/x-vrml"}, + {".xpi", "application/x-xpinstall"}, + {".xpm", "image/x-xpixmap"}, + {".xsit", "text/xml"}, + {".xsl", "text/xml"}, + {".xul", "text/xul"}, + {".xwd", "image/x-xwindowdump"}, + {".xyz", "chemical/x-pdb"}, + {".yz1", "application/x-yz1"}, + {".z", "application/x-compress"}, + {".zac", "application/x-zaurus-zac"}, + {".zip", "application/zip"}, + {".json", "application/json"}, + {".7z", "application/x-7z-compressed"}, +}; + +static std::map res_mime_map = { + {cinatra::req_content_type::html, "text/html; charset=UTF-8"}, + {cinatra::req_content_type::json, "application/json; charset=UTF-8"}, + {cinatra::req_content_type::string, "text/plain; charset=UTF-8"}, + {cinatra::req_content_type::multipart, "multipart/form-data; boundary="}}; + +inline std::string_view get_mime_type(std::string_view extension) { + auto it = mime_map.find(std::string(extension.data(), extension.size())); + if (it == mime_map.end()) { + return "application/octet-stream"; + } + + return it->second; +} +} // namespace cinatra