# HG changeset patch # User Sebastien Jodogne # Date 1439281954 -7200 # Node ID 4f8c8ef114dba419490fc61afba8afd15368a43d # Parent f09f5d3225a76d0dd109bfe3e26a709453db5256 cont diff -r f09f5d3225a7 -r 4f8c8ef114db CMakeLists.txt --- a/CMakeLists.txt Tue Aug 11 08:53:47 2015 +0200 +++ b/CMakeLists.txt Tue Aug 11 10:32:34 2015 +0200 @@ -104,7 +104,6 @@ Core/HttpServer/FilesystemHttpHandler.cpp Core/HttpServer/HttpToolbox.cpp Core/HttpServer/HttpOutput.cpp - Core/HttpServer/IHttpHandler.cpp Core/HttpServer/StringHttpOutput.cpp Core/HttpServer/MongooseServer.cpp Core/HttpServer/HttpFileSender.cpp diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/EmbeddedResourceHttpHandler.cpp --- a/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/EmbeddedResourceHttpHandler.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -81,7 +81,7 @@ size_t size = EmbeddedResources::GetDirectoryResourceSize(resourceId_, resourcePath.c_str()); output.SetContentType(contentType.c_str()); - output.SendBody(buffer, size, IHttpHandler::GetPreferredCompression(headers, size)); + output.SendBody(buffer, size); } catch (OrthancException&) { diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/FilesystemHttpHandler.cpp --- a/Core/HttpServer/FilesystemHttpHandler.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/FilesystemHttpHandler.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -56,8 +56,6 @@ { namespace fs = boost::filesystem; - output.SetContentType("text/html"); - std::string s; s += ""; s += " "; @@ -105,7 +103,8 @@ s += " "; s += ""; - output.SendBody(s, IHttpHandler::GetPreferredCompression(headers, s.size())); + output.SetContentType("text/html"); + output.SendBody(s); } diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/HttpOutput.cpp --- a/Core/HttpServer/HttpOutput.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/HttpOutput.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -215,6 +215,33 @@ } + HttpCompression HttpOutput::GetPreferredCompression(size_t bodySize) const + { +#if 0 + // TODO Do not compress small files? + if (bodySize < 512) + { + return HttpCompression_None; + } +#endif + + // Prefer "gzip" over "deflate" if the choice is offered + + if (isGzipAllowed_) + { + return HttpCompression_Gzip; + } + else if (isDeflateAllowed_) + { + return HttpCompression_Deflate; + } + else + { + return HttpCompression_None; + } + } + + void HttpOutput::SendMethodNotAllowed(const std::string& allowed) { stateMachine_.ClearHeaders(); @@ -259,8 +286,7 @@ } void HttpOutput::SendBody(const void* buffer, - size_t length, - HttpCompression compression) + size_t length) { if (length == 0) { @@ -268,6 +294,8 @@ } else { + HttpCompression compression = GetPreferredCompression(length); + switch (compression) { case HttpCompression_None: @@ -318,10 +346,9 @@ } } - void HttpOutput::SendBody(const std::string& str, - HttpCompression compression) + void HttpOutput::SendBody(const std::string& str) { - SendBody(str.size() == 0 ? NULL : str.c_str(), str.size(), compression); + SendBody(str.size() == 0 ? NULL : str.c_str(), str.size()); } void HttpOutput::SendBody() diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/HttpOutput.h --- a/Core/HttpServer/HttpOutput.h Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/HttpOutput.h Tue Aug 11 10:32:34 2015 +0200 @@ -109,12 +109,38 @@ }; StateMachine stateMachine_; + bool isDeflateAllowed_; + bool isGzipAllowed_; + + HttpCompression GetPreferredCompression(size_t bodySize) const; public: HttpOutput(IHttpOutputStream& stream, bool isKeepAlive) : - stateMachine_(stream, isKeepAlive) + stateMachine_(stream, isKeepAlive), + isDeflateAllowed_(false), + isGzipAllowed_(false) + { + } + + void SetDeflateAllowed(bool allowed) { + isDeflateAllowed_ = allowed; + } + + bool IsDeflateAllowed() const + { + return isDeflateAllowed_; + } + + void SetGzipAllowed(bool allowed) + { + isGzipAllowed_ = allowed; + } + + bool IsGzipAllowed() const + { + return isGzipAllowed_; } void SendStatus(HttpStatus status); @@ -147,11 +173,9 @@ } void SendBody(const void* buffer, - size_t length, - HttpCompression compression = HttpCompression_None); + size_t length); - void SendBody(const std::string& str, - HttpCompression compression = HttpCompression_None); + void SendBody(const std::string& str); void SendBody(); diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/IHttpHandler.cpp --- a/Core/HttpServer/IHttpHandler.cpp Tue Aug 11 08:53:47 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,104 +0,0 @@ -/** - * Orthanc - A Lightweight, RESTful DICOM Store - * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * In addition, as a special exception, the copyright holders of this - * program give permission to link the code of its release with the - * OpenSSL project's "OpenSSL" library (or with modified versions of it - * that use the same license as the "OpenSSL" library), and distribute - * the linked executables. You must obey the GNU General Public License - * in all respects for all of the code used other than "OpenSSL". If you - * modify file(s) with this exception, you may extend this exception to - * your version of the file(s), but you are not obligated to do so. If - * you do not wish to do so, delete this exception statement from your - * version. If you delete this exception statement from all source files - * in the program, then also delete it here. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - **/ - - -#include "../PrecompiledHeaders.h" -#include "IHttpHandler.h" - -#include "../Toolbox.h" - -namespace Orthanc -{ - void IHttpHandler::GetAcceptedCompressions(std::set& result, - const Arguments& headers) - { - // Look if the client wishes HTTP compression - // https://en.wikipedia.org/wiki/HTTP_compression - Arguments::const_iterator it = headers.find("accept-encoding"); - if (it != headers.end()) - { - std::vector encodings; - Toolbox::TokenizeString(encodings, it->second, ','); - for (size_t i = 0; i < encodings.size(); i++) - { - std::string s = Toolbox::StripSpaces(encodings[i]); - - if (s == "deflate") - { - result.insert(HttpCompression_Deflate); - } - else if (s == "gzip") - { - result.insert(HttpCompression_Gzip); - } - } - } - } - - - HttpCompression IHttpHandler::GetPreferredCompression(const Arguments& headers, - size_t bodySize) - { -#if 0 - // TODO - if (bodySize < 1024) - { - // Do not compress small answers - return HttpCompression_None; - } -#endif - - HttpCompression result = HttpCompression_None; - - Arguments::const_iterator it = headers.find("accept-encoding"); - if (it != headers.end()) - { - std::vector encodings; - Toolbox::TokenizeString(encodings, it->second, ','); - for (size_t i = 0; i < encodings.size(); i++) - { - std::string s = Toolbox::StripSpaces(encodings[i]); - - if (s == "deflate" && - result != HttpCompression_Gzip) // Always prefer "gzip" over "deflate" - { - result = HttpCompression_Deflate; - } - else if (s == "gzip") - { - result = HttpCompression_Gzip; - } - } - } - - return result; - } -} diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/IHttpHandler.h --- a/Core/HttpServer/IHttpHandler.h Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/IHttpHandler.h Tue Aug 11 10:32:34 2015 +0200 @@ -59,11 +59,5 @@ const GetArguments& getArguments, const char* bodyData, size_t bodySize) = 0; - - static void GetAcceptedCompressions(std::set& result, - const Arguments& headers); - - static HttpCompression GetPreferredCompression(const Arguments& headers, - size_t bodySize); }; } diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/MongooseServer.cpp --- a/Core/HttpServer/MongooseServer.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/MongooseServer.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -548,6 +548,34 @@ } + static void ConfigureHttpCompression(HttpOutput& output, + const IHttpHandler::Arguments& headers) + { + // Look if the client wishes HTTP compression + // https://en.wikipedia.org/wiki/HTTP_compression + IHttpHandler::Arguments::const_iterator it = headers.find("accept-encoding"); + if (it != headers.end()) + { + std::vector encodings; + Toolbox::TokenizeString(encodings, it->second, ','); + + for (size_t i = 0; i < encodings.size(); i++) + { + std::string s = Toolbox::StripSpaces(encodings[i]); + + if (s == "deflate") + { + output.SetDeflateAllowed(true); + } + else if (s == "gzip") + { + output.SetGzipAllowed(true); + } + } + } + } + + static void InternalCallback(struct mg_connection *connection, const struct mg_request_info *request) { @@ -574,6 +602,11 @@ headers.insert(std::make_pair(name, request->http_headers[i].value)); } + if (that->IsHttpCompressionEnabled()) + { + ConfigureHttpCompression(output, headers); + } + // Extract the GET arguments IHttpHandler::GetArguments argumentsGET; @@ -799,6 +832,7 @@ port_ = 8000; filter_ = NULL; keepAlive_ = false; + httpCompression_ = true; #if ORTHANC_SSL_ENABLED == 1 // Check for the Heartbleed exploit @@ -942,6 +976,12 @@ remoteAllowed_ = allowed; } + void MongooseServer::SetHttpCompressionEnabled(bool enabled) + { + Stop(); + httpCompression_ = enabled; + } + void MongooseServer::SetIncomingHttpRequestFilter(IIncomingHttpRequestFilter& filter) { Stop(); diff -r f09f5d3225a7 -r 4f8c8ef114db Core/HttpServer/MongooseServer.h --- a/Core/HttpServer/MongooseServer.h Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/HttpServer/MongooseServer.h Tue Aug 11 10:32:34 2015 +0200 @@ -76,6 +76,7 @@ uint16_t port_; IIncomingHttpRequestFilter* filter_; bool keepAlive_; + bool httpCompression_; bool IsRunning() const; @@ -135,6 +136,13 @@ void SetRemoteAccessAllowed(bool allowed); + bool IsHttpCompressionEnabled() const + { + return httpCompression_;; + } + + void SetHttpCompressionEnabled(bool enabled); + const IIncomingHttpRequestFilter* GetIncomingHttpRequestFilter() const { return filter_; diff -r f09f5d3225a7 -r 4f8c8ef114db Core/RestApi/RestApi.cpp --- a/Core/RestApi/RestApi.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/RestApi/RestApi.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -194,20 +194,6 @@ } #endif - std::set compressions; - GetAcceptedCompressions(compressions, headers); - - if (compressions.find(HttpCompression_Deflate) != compressions.end()) - { - wrappedOutput.AllowDeflateCompression(true); - } - - if (compressions.find(HttpCompression_Gzip) != compressions.end()) - { - wrappedOutput.AllowGzipCompression(true); - } - - Arguments compiled; HttpToolbox::CompileGetArguments(compiled, getArguments); diff -r f09f5d3225a7 -r 4f8c8ef114db Core/RestApi/RestApiOutput.cpp --- a/Core/RestApi/RestApiOutput.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/Core/RestApi/RestApiOutput.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -117,13 +117,13 @@ { CheckStatus(); - std::string s; - if (convertJsonToXml_) { #if ORTHANC_PUGIXML_ENABLED == 1 + std::string s; Toolbox::JsonToXml(s, value); output_.SetContentType("application/xml"); + output_.SendBody(s); #else LOG(ERROR) << "Orthanc was compiled without XML support"; throw OrthancException(ErrorCode_InternalError); @@ -133,11 +133,9 @@ { Json::StyledWriter writer; output_.SetContentType("application/json"); - s = writer.write(value); + output_.SendBody(writer.write(value)); } - output_.SendBody(s, GetPreferredCompression(s.size())); - alreadySent_ = true; } @@ -154,7 +152,7 @@ { CheckStatus(); output_.SetContentType(contentType.c_str()); - output_.SendBody(buffer, length, GetPreferredCompression(length)); + output_.SendBody(buffer, length); alreadySent_ = true; } diff -r f09f5d3225a7 -r 4f8c8ef114db OrthancServer/main.cpp --- a/OrthancServer/main.cpp Tue Aug 11 08:53:47 2015 +0200 +++ b/OrthancServer/main.cpp Tue Aug 11 10:32:34 2015 +0200 @@ -421,6 +421,7 @@ httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042)); httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false)); httpServer.SetKeepAliveEnabled(Configuration::GetGlobalBoolParameter("KeepAlive", false)); + httpServer.SetHttpCompressionEnabled(Configuration::GetGlobalBoolParameter("HttpCompressionEnabled", true)); httpServer.SetIncomingHttpRequestFilter(httpFilter); httpServer.SetAuthenticationEnabled(Configuration::GetGlobalBoolParameter("AuthenticationEnabled", false)); diff -r f09f5d3225a7 -r 4f8c8ef114db Resources/Configuration.json --- a/Resources/Configuration.json Tue Aug 11 08:53:47 2015 +0200 +++ b/Resources/Configuration.json Tue Aug 11 10:32:34 2015 +0200 @@ -232,5 +232,10 @@ // When handling a C-Find SCP request, setting this flag to "false" // will enable case-insensitive match for PN value representation // (such as PatientName). By default, the search is case-insensitive. - "CaseSensitivePN" : false + "CaseSensitivePN" : false, + + // Enable HTTP compression to improve network bandwidth utilization, + // at the expense of more computations on the server. Orthanc + // supports the "gzip" and "deflate" encodings. + "HttpCompressionEnabled" : true }