diff --git a/src/server/campaignd/server.cpp b/src/server/campaignd/server.cpp index 3ffe26645774..71450f85ef07 100644 --- a/src/server/campaignd/server.cpp +++ b/src/server/campaignd/server.cpp @@ -1149,16 +1149,11 @@ void server::handle_request_campaign(const server::request& req) } LOG_CS << req << "Sending add-on '" << name << "' version: " << to << " size: " << full_pack_size / 1024 << " KiB\n"; - if(auto sock = utils::get_if(&req.sock)) { + utils::visit([this, &req, &full_pack_path](auto&& socket) { boost::system::error_code ec; - coro_send_file(*sock, full_pack_path, req.yield[ec]); - if(check_error(ec, *sock)) return; - } else { - // FIXME: need to decide what to do about sending addons over TLS. - // sendfile is low level api so either drop TLS for this part - // or don't use sendfile at all. - ERR_CS << "Not sending addon over TLS yet\n"; - } + coro_send_file(socket, full_pack_path, req.yield[ec]); + if(check_error(ec, socket)) return; + }, req.sock); } // Clients doing upgrades or some other specific thing shouldn't bump @@ -1210,13 +1205,11 @@ void server::handle_request_campaign_hash(const server::request& req) } LOG_CS << req << "Sending add-on hash index for '" << req.cfg["name"] << "' size: " << file_size / 1024 << " KiB\n"; - if(auto sock = utils::get_if(&req.sock)) { + utils::visit([this, &path, &req](auto&& socket) { boost::system::error_code ec; - coro_send_file(*sock, path, req.yield[ec]); - if(check_error(ec, *sock)) return; - } else { - ERR_CS << "No sendfile over TLS\n"; - } + coro_send_file(socket, path, req.yield[ec]); + if(check_error(ec, socket)) return; + }, req.sock); } } diff --git a/src/server/common/server_base.cpp b/src/server/common/server_base.cpp index 98e14c203719..0881dab399e0 100644 --- a/src/server/common/server_base.cpp +++ b/src/server/common/server_base.cpp @@ -309,10 +309,38 @@ template void server_base::coro_send_doc(SocketPtr socket, simp template void server_base::coro_send_doc(socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield); template void server_base::coro_send_doc(tls_socket_ptr socket, simple_wml::document& doc, boost::asio::yield_context yield); +template void coro_send_file_userspace(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) +{ + std::size_t filesize { std::size_t(filesystem::file_size(filename)) }; + union DataSize + { + uint32_t size; + char buf[4]; + } data_size {}; + data_size.size = htonl(filesize); + + async_write(*socket, boost::asio::buffer(data_size.buf), yield); + + auto ifs { filesystem::istream_file(filename) }; + ifs->seekg(0); + while(ifs->good()) { + char buf[16384]; + ifs->read(buf, sizeof(buf)); + async_write(*socket, boost::asio::buffer(buf, ifs->gcount()), yield); + } +} + #ifdef HAVE_SENDFILE template void server_base::coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) { + // We fallback to userspace if using TLS socket because sendfile is not aware of TLS state + // TODO: keep in mind possibility of using KTLS instead. This seem to be available only in openssl3 branch for now + if constexpr (utils::decayed_is_same) { + coro_send_file_userspace(socket, filename, yield); + return; + } else { + std::size_t filesize { std::size_t(filesystem::file_size(filename)) }; int in_file { open(filename.c_str(), O_RDONLY) }; off_t offset { 0 }; @@ -364,12 +392,17 @@ template void server_base::coro_send_file(SocketPtr socket, con // Loop around to try calling sendfile again. } + } } #elif defined(_WIN32) template void server_base::coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) { + if constexpr (utils::decayed_is_same) { + coro_send_file_userspace(socket, filename, yield); + return; + } else { OVERLAPPED overlap; std::vector buffers; @@ -425,19 +458,20 @@ template void server_base::coro_send_file(SocketPtr socket, con throw std::runtime_error("TransmitFile failed"); } } + } } #else template void server_base::coro_send_file(SocketPtr socket, const std::string& filename, boost::asio::yield_context yield) { -// TODO: Implement this for systems without sendfile() - assert(false && "Not implemented yet"); + coro_send_file_userspace(socket, filename, yield); } #endif template void server_base::coro_send_file(socket_ptr socket, const std::string& filename, boost::asio::yield_context yield); +template void server_base::coro_send_file(tls_socket_ptr socket, const std::string& filename, boost::asio::yield_context yield); template std::unique_ptr server_base::coro_receive_doc(SocketPtr socket, boost::asio::yield_context yield) {