changeset 2069:fabf7820d1f1

New configuration options: "DicomScuTimeout" and "DicomScpTimeout" + validation of non-negative options
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 13 Jul 2016 16:52:39 +0200
parents 879f3be759ef
children 7e6afa0beaf6
files Core/HttpServer/MongooseServer.cpp NEWS OrthancServer/DicomProtocol/DicomServer.cpp OrthancServer/DicomProtocol/DicomServer.h OrthancServer/DicomProtocol/DicomUserConnection.cpp OrthancServer/DicomProtocol/DicomUserConnection.h OrthancServer/OrthancInitialization.cpp OrthancServer/OrthancInitialization.h OrthancServer/OrthancRestApi/OrthancRestSystem.cpp OrthancServer/ServerContext.cpp OrthancServer/ServerIndex.cpp OrthancServer/main.cpp Resources/Configuration.json
diffstat 13 files changed, 80 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/Core/HttpServer/MongooseServer.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/Core/HttpServer/MongooseServer.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -951,7 +951,7 @@
   {
     Stop();
     keepAlive_ = enabled;
-    LOG(WARNING) << "HTTP keep alive is " << (enabled ? "enabled" : "disabled");
+    LOG(INFO) << "HTTP keep alive is " << (enabled ? "enabled" : "disabled");
   }
 
 
--- a/NEWS	Wed Jul 13 16:10:25 2016 +0200
+++ b/NEWS	Wed Jul 13 16:52:39 2016 +0200
@@ -2,6 +2,7 @@
 ===============================
 
 * "Permissive" flag for URI "/modalities/{...}/store" to ignore C-Store transfer errors
+* New configuration options: "DicomScuTimeout" and "DicomScpTimeout"
 
 
 Version 1.1.0 (2016/06/27)
--- a/OrthancServer/DicomProtocol/DicomServer.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -121,10 +121,13 @@
     return port_;
   }
 
-  void DicomServer::SetAssociationTimeout(uint32_t timeout)
+  void DicomServer::SetAssociationTimeout(uint32_t seconds)
   {
+    LOG(INFO) << "Setting timeout for DICOM connections if Orthanc acts as SCP (server): " 
+              << seconds << " seconds (0 = no timeout)";
+
     Stop();
-    associationTimeout_ = timeout;
+    associationTimeout_ = seconds;
   }
 
   uint32_t DicomServer::GetAssociationTimeout() const
@@ -296,15 +299,9 @@
   {
     Stop();
 
-    uint32_t timeout = associationTimeout_;
-    if (timeout == 0)
-    {
-      timeout = 30;  // Some safe value (30 seconds) if association timeout is disabled
-    }
-
     /* initialize network, i.e. create an instance of T_ASC_Network*. */
     OFCondition cond = ASC_initializeNetwork
-      (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ timeout, &pimpl_->network_);
+      (NET_ACCEPTOR, OFstatic_cast(int, port_), /*opt_acse_timeout*/ 30, &pimpl_->network_);
     if (cond.bad())
     {
       LOG(ERROR) << "cannot create network: " << cond.text();
--- a/OrthancServer/DicomProtocol/DicomServer.h	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.h	Wed Jul 13 16:52:39 2016 +0200
@@ -71,7 +71,7 @@
     void SetPortNumber(uint16_t port);
     uint16_t GetPortNumber() const;
 
-    void SetAssociationTimeout(uint32_t timeout);
+    void SetAssociationTimeout(uint32_t seconds);
     uint32_t GetAssociationTimeout() const;
 
     void SetCalledApplicationEntityTitleCheck(bool check);
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -133,6 +133,9 @@
 
 namespace Orthanc
 {
+  // By default, the timeout for DICOM SCU (client) connections is set to 10 seconds
+  static uint32_t defaultTimeout_ = 10;
+
   struct DicomUserConnection::PImpl
   {
     // Connection state
@@ -782,7 +785,7 @@
     remotePort_ = 104;
     manufacturer_ = ModalityManufacturer_Generic;
 
-    SetTimeout(10); 
+    SetTimeout(defaultTimeout_);
     pimpl_->net_ = NULL;
     pimpl_->params_ = NULL;
     pimpl_->assoc_ = NULL;
@@ -1102,14 +1105,16 @@
 
   void DicomUserConnection::SetTimeout(uint32_t seconds)
   {
-    if (seconds <= 0)
+    if (seconds == 0)
     {
-      throw OrthancException(ErrorCode_ParameterOutOfRange);
+      DisableTimeout();
     }
-
-    dcmConnectionTimeout.set(seconds);
-    pimpl_->dimseTimeout_ = seconds;
-    pimpl_->acseTimeout_ = 10;
+    else
+    {
+      dcmConnectionTimeout.set(seconds);
+      pimpl_->dimseTimeout_ = seconds;
+      pimpl_->acseTimeout_ = 10;  // Timeout used during association negociation
+    }
   }
 
 
@@ -1121,7 +1126,7 @@
      */
     dcmConnectionTimeout.set(-1);
     pimpl_->dimseTimeout_ = 0;
-    pimpl_->acseTimeout_ = 10;
+    pimpl_->acseTimeout_ = 10;  // Timeout used during association negociation
   }
 
 
@@ -1191,4 +1196,12 @@
 
     ExecuteFind(result, pimpl_->assoc_, dataset, sopClass, true, NULL, pimpl_->dimseTimeout_);
   }
+
+  
+  void DicomUserConnection::SetDefaultTimeout(uint32_t seconds)
+  {
+    LOG(INFO) << "Default timeout for DICOM connections if Orthanc acts as SCU (client): " 
+              << seconds << " seconds (0 = no timeout)";
+    defaultTimeout_ = seconds;
+  }  
 }
--- a/OrthancServer/DicomProtocol/DicomUserConnection.h	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.h	Wed Jul 13 16:52:39 2016 +0200
@@ -175,5 +175,7 @@
 
     void FindWorklist(DicomFindAnswers& result,
                       ParsedDicomFile& query);
+
+    static void SetDefaultTimeout(uint32_t seconds);
   };
 }
--- a/OrthancServer/OrthancInitialization.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/OrthancInitialization.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -577,6 +577,23 @@
   }
 
 
+  unsigned int Configuration::GetGlobalUnsignedIntegerParameter(const std::string& parameter,
+                                                                unsigned int defaultValue)
+  {
+    int v = GetGlobalIntegerParameter(parameter, defaultValue);
+
+    if (v < 0)
+    {
+      LOG(ERROR) << "The configuration option \"" << parameter << "\" must be a positive integer";
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+    else
+    {
+      return static_cast<unsigned int>(v);
+    }
+  }
+
+
   bool Configuration::GetGlobalBoolParameter(const std::string& parameter,
                                              bool defaultValue)
   {
--- a/OrthancServer/OrthancInitialization.h	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/OrthancInitialization.h	Wed Jul 13 16:52:39 2016 +0200
@@ -64,6 +64,9 @@
     static int GetGlobalIntegerParameter(const std::string& parameter,
                                          int defaultValue);
 
+    static unsigned int GetGlobalUnsignedIntegerParameter(const std::string& parameter,
+                                                          unsigned int defaultValue);
+
     static bool GetGlobalBoolParameter(const std::string& parameter,
                                        bool defaultValue);
 
--- a/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestSystem.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -55,8 +55,8 @@
 
     result["DatabaseVersion"] = OrthancRestApi::GetIndex(call).GetDatabaseVersion();
     result["DicomAet"] = Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC");
-    result["DicomPort"] = Configuration::GetGlobalIntegerParameter("DicomPort", 4242);
-    result["HttpPort"] = Configuration::GetGlobalIntegerParameter("HttpPort", 8042);
+    result["DicomPort"] = Configuration::GetGlobalUnsignedIntegerParameter("DicomPort", 4242);
+    result["HttpPort"] = Configuration::GetGlobalUnsignedIntegerParameter("HttpPort", 8042);
     result["Name"] = Configuration::GetGlobalStringParameter("Name", "");
     result["Version"] = ORTHANC_VERSION;
 
--- a/OrthancServer/ServerContext.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/ServerContext.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -106,16 +106,16 @@
     storeMD5_(true),
     provider_(*this),
     dicomCache_(provider_, DICOM_CACHE_SIZE),
-    scheduler_(Configuration::GetGlobalIntegerParameter("LimitJobs", 10)),
+    scheduler_(Configuration::GetGlobalUnsignedIntegerParameter("LimitJobs", 10)),
     lua_(*this),
 #if ORTHANC_PLUGINS_ENABLED == 1
     plugins_(NULL),
 #endif
     done_(false),
-    queryRetrieveArchive_(Configuration::GetGlobalIntegerParameter("QueryRetrieveSize", 10)),
+    queryRetrieveArchive_(Configuration::GetGlobalUnsignedIntegerParameter("QueryRetrieveSize", 10)),
     defaultLocalAet_(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"))
   {
-    uint64_t s = Configuration::GetGlobalIntegerParameter("DicomAssociationCloseDelay", 5);  // In seconds
+    uint64_t s = Configuration::GetGlobalUnsignedIntegerParameter("DicomAssociationCloseDelay", 5);  // In seconds
     scu_.SetMillisecondsBeforeClose(s * 1000);  // Milliseconds are expected here
 
     listeners_.push_back(ServerListener(lua_, "Lua"));
--- a/OrthancServer/ServerIndex.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/ServerIndex.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -1848,7 +1848,7 @@
 
   void ServerIndex::UnstableResourcesMonitorThread(ServerIndex* that)
   {
-    int stableAge = Configuration::GetGlobalIntegerParameter("StableAge", 60);
+    int stableAge = Configuration::GetGlobalUnsignedIntegerParameter("StableAge", 60);
     if (stableAge <= 0)
     {
       stableAge = 60;
--- a/OrthancServer/main.cpp	Wed Jul 13 16:10:25 2016 +0200
+++ b/OrthancServer/main.cpp	Wed Jul 13 16:52:39 2016 +0200
@@ -111,8 +111,8 @@
   {
     std::auto_ptr<OrthancFindRequestHandler> result(new OrthancFindRequestHandler(context_));
 
-    result->SetMaxResults(Configuration::GetGlobalIntegerParameter("LimitFindResults", 0));
-    result->SetMaxInstances(Configuration::GetGlobalIntegerParameter("LimitFindInstances", 0));
+    result->SetMaxResults(Configuration::GetGlobalUnsignedIntegerParameter("LimitFindResults", 0));
+    result->SetMaxInstances(Configuration::GetGlobalUnsignedIntegerParameter("LimitFindInstances", 0));
 
     if (result->GetMaxResults() == 0)
     {
@@ -722,7 +722,7 @@
   // HTTP server
   MyIncomingHttpRequestFilter httpFilter(context, plugins);
   MongooseServer httpServer;
-  httpServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("HttpPort", 8042));
+  httpServer.SetPortNumber(Configuration::GetGlobalUnsignedIntegerParameter("HttpPort", 8042));
   httpServer.SetRemoteAccessAllowed(Configuration::GetGlobalBoolParameter("RemoteAccessAllowed", false));
   httpServer.SetKeepAliveEnabled(Configuration::GetGlobalBoolParameter("KeepAlive", false));
   httpServer.SetHttpCompressionEnabled(Configuration::GetGlobalBoolParameter("HttpCompressionEnabled", true));
@@ -784,6 +784,8 @@
   dicomServer.SetStoreRequestHandlerFactory(serverFactory);
   dicomServer.SetMoveRequestHandlerFactory(serverFactory);
   dicomServer.SetFindRequestHandlerFactory(serverFactory);
+  dicomServer.SetAssociationTimeout(Configuration::GetGlobalUnsignedIntegerParameter("DicomScpTimeout", 30));
+
 
 #if ORTHANC_PLUGINS_ENABLED == 1
   if (plugins != NULL)
@@ -805,7 +807,7 @@
   }
 #endif
 
-  dicomServer.SetPortNumber(Configuration::GetGlobalIntegerParameter("DicomPort", 4242));
+  dicomServer.SetPortNumber(Configuration::GetGlobalUnsignedIntegerParameter("DicomPort", 4242));
   dicomServer.SetApplicationEntityTitle(Configuration::GetGlobalStringParameter("DicomAet", "ORTHANC"));
   dicomServer.SetApplicationEntityFilter(dicomFilter);
 
@@ -936,14 +938,17 @@
 
   HttpClient::ConfigureSsl(Configuration::GetGlobalBoolParameter("HttpsVerifyPeers", true),
                            Configuration::GetGlobalStringParameter("HttpsCACertificates", ""));
-  HttpClient::SetDefaultTimeout(Configuration::GetGlobalIntegerParameter("HttpTimeout", 0));
+  HttpClient::SetDefaultTimeout(Configuration::GetGlobalUnsignedIntegerParameter("HttpTimeout", 0));
   HttpClient::SetDefaultProxy(Configuration::GetGlobalStringParameter("HttpProxy", ""));
+
+  DicomUserConnection::SetDefaultTimeout(Configuration::GetGlobalUnsignedIntegerParameter("DicomScuTimeout", 10));
+
   context.SetCompressionEnabled(Configuration::GetGlobalBoolParameter("StorageCompression", false));
   context.SetStoreMD5ForAttachments(Configuration::GetGlobalBoolParameter("StoreMD5ForAttachments", true));
 
   try
   {
-    context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalIntegerParameter("MaximumPatientCount", 0));
+    context.GetIndex().SetMaximumPatientCount(Configuration::GetGlobalUnsignedIntegerParameter("MaximumPatientCount", 0));
   }
   catch (...)
   {
@@ -952,7 +957,7 @@
 
   try
   {
-    uint64_t size = Configuration::GetGlobalIntegerParameter("MaximumStorageSize", 0);
+    uint64_t size = Configuration::GetGlobalUnsignedIntegerParameter("MaximumStorageSize", 0);
     context.GetIndex().SetMaximumStorageSize(size * 1024 * 1024);
   }
   catch (...)
--- a/Resources/Configuration.json	Wed Jul 13 16:10:25 2016 +0200
+++ b/Resources/Configuration.json	Wed Jul 13 16:52:39 2016 +0200
@@ -106,6 +106,11 @@
   // SOP classes (aka. "promiscuous mode")
   "UnknownSopClassAccepted"            : false,
 
+  // Set the timeout (in seconds) after which the DICOM associations
+  // are closed by the Orthanc SCP (server) if no further DIMSE
+  // command is received from the SCU (client).
+  "DicomScpTimeout" : 30,
+
 
 
   /**
@@ -158,6 +163,11 @@
     // "clearcanvas" : [ "CLEARCANVAS", "192.168.1.1", 104, "ClearCanvas" ]
   },
 
+  // The timeout (in seconds) after which the DICOM associations are
+  // considered as closed by the Orthanc SCU (client) if the remote
+  // DICOM SCP (server) does not answer.
+  "DicomScuTimeout" : 10,
+
   // The list of the known Orthanc peers
   "OrthancPeers" : {
     /**