diff --git a/xbmc/filesystem/ZipFile.cpp b/xbmc/filesystem/ZipFile.cpp index 225e9fd624929..43e9eb37141d1 100644 --- a/xbmc/filesystem/ZipFile.cpp +++ b/xbmc/filesystem/ZipFile.cpp @@ -525,3 +525,40 @@ bool CZipFile::DecompressGzip(const std::string& in, std::string& out) inflateEnd(&strm); return true; } + +bool CZipFile::CompressGZip(const std::string& in, std::string& out, int compressionLevel) +{ + const int windowBits = MAX_WBITS + 16; + + z_stream strm; + memset(&strm, 0, sizeof(strm)); + + int err = deflateInit2(&strm, compressionLevel, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + { + CLog::Log(LOGERROR, "FileZip: zlib compress error %d", err); + return false; + } + + strm.avail_in = static_cast(in.size()); + strm.next_in = reinterpret_cast(const_cast(in.c_str())); + + char outbuffer[32768]; + do + { + strm.next_out = reinterpret_cast(outbuffer); + strm.avail_out = sizeof(outbuffer); + err = deflate(&strm, Z_FINISH); + if (out.size() < strm.total_out) + out.append(outbuffer, strm.total_out - out.size()); + } while (err == Z_OK); + + deflateEnd(&strm); + + if (err != Z_STREAM_END) + { + CLog::Log(LOGERROR, "FileZip: failed to compress. zlib error %d", err); + return false; + } + return true; +} diff --git a/xbmc/filesystem/ZipFile.h b/xbmc/filesystem/ZipFile.h index 1b09e5c80bfa8..1263bd96071ce 100644 --- a/xbmc/filesystem/ZipFile.h +++ b/xbmc/filesystem/ZipFile.h @@ -38,6 +38,8 @@ namespace XFILE /*! Decompress gzip encoded buffer in-memory */ static bool DecompressGzip(const std::string& in, std::string& out); + /*! Compress to gzip encoded buffer in-memory */ + static bool CompressGZip(const std::string& in, std::string& out, int compressionLevel); private: bool InitDecompress(); diff --git a/xbmc/network/WebServer.cpp b/xbmc/network/WebServer.cpp index 768e59426f5fb..99862d5704d3b 100644 --- a/xbmc/network/WebServer.cpp +++ b/xbmc/network/WebServer.cpp @@ -362,6 +362,10 @@ int CWebServer::FinalizeRequest(const std::shared_ptr& hand if (!responseDetails.contentType.empty()) handler->AddResponseHeader(MHD_HTTP_HEADER_CONTENT_TYPE, responseDetails.contentType); + // if the request handler has set a content encoding and it hasn't been set as a header, add it + if (!responseDetails.contentEncoding.empty()) + handler->AddResponseHeader(MHD_HTTP_HEADER_CONTENT_ENCODING, responseDetails.contentEncoding); + // if the request handler has set a last modified date and it hasn't been set as a header, add it CDateTime lastModified; if (handler->GetLastModifiedDate(lastModified) && lastModified.IsValid()) diff --git a/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp b/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp index 5ac94cc29286b..c6074464be3b9 100644 --- a/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp +++ b/xbmc/network/httprequesthandler/HTTPJsonRpcHandler.cpp @@ -10,11 +10,14 @@ #include "URL.h" #include "filesystem/File.h" +#include "filesystem/ZipFile.h" #include "interfaces/json-rpc/JSONRPC.h" #include "interfaces/json-rpc/JSONServiceDescription.h" #include "interfaces/json-rpc/JSONUtils.h" #include "network/WebServer.h" #include "network/httprequesthandler/HTTPRequestHandlerUtils.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" #include "utils/JSONVariantWriter.h" #include "utils/Variant.h" #include "utils/log.h" @@ -99,6 +102,30 @@ int CHTTPJsonRpcHandler::HandleRequest() return MHD_YES; } + std::string acceptEncoding = HTTPRequestHandlerUtils::GetRequestHeaderValue( + m_request.connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_ACCEPT_ENCODING); + if (isRequest && acceptEncoding.find("gzip") != std::string::npos) + { + int compressionLevel = + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_jsonCompressionLevel; + if (compressionLevel != 0) + { + unsigned int time = XbmcThreads::SystemClockMillis(); + std::string gzipAnswer; + auto uncompressedSize = m_responseData.size(); + if (XFILE::CZipFile::CompressGZip(m_responseData, gzipAnswer, compressionLevel)) + { + m_responseData = std::move(gzipAnswer); + m_response.contentEncoding = "gzip"; + unsigned int duration = XbmcThreads::SystemClockMillis() - time; + if (duration > 1) + CLog::Log(LOGINFO, "{0} - Compress time {1}ms [{2} -> {3} R{5}:{4:.2f}]", __FUNCTION__, + duration, uncompressedSize, m_responseData.size(), + static_cast(uncompressedSize) / m_responseData.size(), compressionLevel); + } + } + } + m_requestData.clear(); m_responseRange.SetData(m_responseData.c_str(), m_responseData.size()); diff --git a/xbmc/network/httprequesthandler/IHTTPRequestHandler.h b/xbmc/network/httprequesthandler/IHTTPRequestHandler.h index e62795f3fab89..08d4b70afc6f8 100644 --- a/xbmc/network/httprequesthandler/IHTTPRequestHandler.h +++ b/xbmc/network/httprequesthandler/IHTTPRequestHandler.h @@ -74,6 +74,7 @@ typedef struct HTTPResponseDetails { std::multimap headers; std::string contentType; uint64_t totalLength; + std::string contentEncoding; } HTTPResponseDetails; class IHTTPRequestHandler diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp index bf7c76a5172f5..5b3b4bf1b86e9 100644 --- a/xbmc/settings/AdvancedSettings.cpp +++ b/xbmc/settings/AdvancedSettings.cpp @@ -393,6 +393,7 @@ void CAdvancedSettings::Initialize() m_jsonOutputCompact = true; m_jsonTcpPort = 9090; + m_jsonCompressionLevel = 4; m_enableMultimediaKeys = false; @@ -834,6 +835,7 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file) { XMLUtils::GetBoolean(pElement, "compactoutput", m_jsonOutputCompact); XMLUtils::GetUInt(pElement, "tcpport", m_jsonTcpPort); + XMLUtils::GetInt(pElement, "compressionlevel", m_jsonCompressionLevel); } pElement = pRootElement->FirstChildElement("samba"); diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h index 4949e9c764ce9..8a85f55f4d235 100644 --- a/xbmc/settings/AdvancedSettings.h +++ b/xbmc/settings/AdvancedSettings.h @@ -334,6 +334,7 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler bool m_jsonOutputCompact; unsigned int m_jsonTcpPort; + int m_jsonCompressionLevel; /*!< @brief zLib compression level from -1 to 9. (0 disable compression support) */ bool m_enableMultimediaKeys; std::vector m_settingsFiles;