changeset 941:83489fddd8c5

Options to limit the number of results for an incoming C-FIND query
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Jun 2014 11:08:11 +0200
parents 4864b3e304be
children b3f6fb1130cd
files NEWS OrthancServer/DicomProtocol/IFindRequestHandler.h OrthancServer/Internals/FindScp.cpp OrthancServer/OrthancFindRequestHandler.cpp OrthancServer/OrthancFindRequestHandler.h OrthancServer/main.cpp Resources/Configuration.json
diffstat 7 files changed, 115 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Wed Jun 25 09:06:34 2014 +0200
+++ b/NEWS	Wed Jun 25 11:08:11 2014 +0200
@@ -2,6 +2,7 @@
 ===============================
 
 * Official support of OS X (Darwin)
+* Options to limit the number of results for an incoming C-FIND query
 * Support of kFreeBSD
 
 
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h	Wed Jun 25 09:06:34 2014 +0200
+++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h	Wed Jun 25 11:08:11 2014 +0200
@@ -47,7 +47,13 @@
     {
     }
 
-    virtual void Handle(DicomFindAnswers& answers,
+    /**
+     * Can throw exceptions. Returns "false" iff too many results have
+     * to be returned. In such a case, a "Matching terminated due to
+     * Cancel request" DIMSE code would be returned.
+     * https://www.dabsoft.ch/dicom/4/V.4.1/
+     **/
+    virtual bool Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
                         const std::string& callingAETitle) = 0;
   };
--- a/OrthancServer/Internals/FindScp.cpp	Wed Jun 25 09:06:34 2014 +0200
+++ b/OrthancServer/Internals/FindScp.cpp	Wed Jun 25 11:08:11 2014 +0200
@@ -100,6 +100,7 @@
       DicomFindAnswers answers_;
       DcmDataset* lastRequest_;
       const std::string* callingAETitle_;
+      bool noCroppingOfResults_;
     };
 
 
@@ -125,12 +126,12 @@
 
         try
         {
-          data.handler_->Handle(data.answers_, data.input_, *data.callingAETitle_);
+          data.noCroppingOfResults_ = data.handler_->Handle(data.answers_, data.input_, *data.callingAETitle_);
         }
         catch (OrthancException& e)
         {
           // Internal error!
-          LOG(ERROR) <<  "IFindRequestHandler Failed: " << e.What();
+          LOG(ERROR) <<  "C-FIND request handler has failed: " << e.What();
           response->DimseStatus = STATUS_FIND_Failed_UnableToProcess;
           *responseIdentifiers = NULL;   
           return;
@@ -148,12 +149,21 @@
 
       if (responseCount <= static_cast<int>(data.answers_.GetSize()))
       {
+        // There are pending results that are still to be sent
         response->DimseStatus = STATUS_Pending;
         *responseIdentifiers = ToDcmtkBridge::Convert(data.answers_.GetAnswer(responseCount - 1));
       }
+      else if (data.noCroppingOfResults_)
+      {
+        // Success: All the results have been sent
+        response->DimseStatus = STATUS_Success;
+        *responseIdentifiers = NULL;
+      }
       else
       {
-        response->DimseStatus = STATUS_Success;
+        // Success, but the results were too numerous and had to be cropped
+        LOG(WARNING) <<  "Too many results for an incoming C-FIND query";
+        response->DimseStatus = STATUS_FIND_Cancel_MatchingTerminatedDueToCancelRequest;
         *responseIdentifiers = NULL;
       }
     }
@@ -170,6 +180,7 @@
     data.lastRequest_ = NULL;
     data.handler_ = &handler;
     data.callingAETitle_ = &callingAETitle;
+    data.noCroppingOfResults_ = true;
 
     OFCondition cond = DIMSE_findProvider(assoc, presID, &msg->msg.CFindRQ, 
                                           FindScpCallback, &data,
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Wed Jun 25 09:06:34 2014 +0200
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Wed Jun 25 11:08:11 2014 +0200
@@ -442,7 +442,26 @@
   }
 
 
-  void OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
+  bool OrthancFindRequestHandler::HasReachedLimit(const DicomFindAnswers& answers,
+                                                  ResourceType level) const
+  {
+    switch (level)
+    {
+      case ResourceType_Patient:
+      case ResourceType_Study:
+      case ResourceType_Series:
+        return (maxResults_ != 0 && answers.GetSize() >= maxResults_);
+
+      case ResourceType_Instance:
+        return (maxInstances_ != 0 && answers.GetSize() >= maxInstances_);
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
+  bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
                                          const DicomMap& input,
                                          const std::string& callingAETitle)
   {
@@ -580,6 +599,12 @@
         
           if (Matches(info, query))
           {
+            if (HasReachedLimit(answers, level))
+            {
+              // Too many results, stop before recording this new match
+              return false;
+            }
+
             AddAnswer(answers, info, query);
           }
         }
@@ -589,6 +614,8 @@
         // This resource has probably been deleted during the find request
       }
     }
+
+    return true;  // All the matching resources have been returned
   }
 }
 
--- a/OrthancServer/OrthancFindRequestHandler.h	Wed Jun 25 09:06:34 2014 +0200
+++ b/OrthancServer/OrthancFindRequestHandler.h	Wed Jun 25 11:08:11 2014 +0200
@@ -41,15 +41,42 @@
   {
   private:
     ServerContext& context_;
+    unsigned int maxResults_;
+    unsigned int maxInstances_;
+
+    bool HasReachedLimit(const DicomFindAnswers& answers,
+                         ResourceType level) const;
 
   public:
     OrthancFindRequestHandler(ServerContext& context) :
-    context_(context)
+      context_(context), 
+      maxResults_(0),
+      maxInstances_(0)
     {
     }
 
-    virtual void Handle(DicomFindAnswers& answers,
+    virtual bool Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
                         const std::string& callingAETitle);
+
+    unsigned int GetMaxResults() const
+    {
+      return maxResults_;
+    }
+
+    void SetMaxResults(unsigned int results)
+    {
+      maxResults_ = results;
+    }
+
+    unsigned int GetMaxInstances() const
+    {
+      return maxInstances_;
+    }
+
+    void SetMaxInstances(unsigned int instances)
+    {
+      maxInstances_ = instances;
+    }
   };
 }
--- a/OrthancServer/main.cpp	Wed Jun 25 09:06:34 2014 +0200
+++ b/OrthancServer/main.cpp	Wed Jun 25 11:08:11 2014 +0200
@@ -98,7 +98,32 @@
 
   virtual IFindRequestHandler* ConstructFindRequestHandler()
   {
-    return new OrthancFindRequestHandler(context_);
+    std::auto_ptr<OrthancFindRequestHandler> result(new OrthancFindRequestHandler(context_));
+
+    result->SetMaxResults(Configuration::GetGlobalIntegerParameter("LimitFindResults", 0));
+    result->SetMaxInstances(Configuration::GetGlobalIntegerParameter("LimitFindInstances", 0));
+
+    if (result->GetMaxResults() == 0)
+    {
+      LOG(INFO) << "No limit on the number of C-FIND results at the Patient, Study and Series levels";
+    }
+    else
+    {
+      LOG(INFO) << "Maximum " << result->GetMaxResults() 
+                << " results for C-FIND queries at the Patient, Study and Series levels";
+    }
+
+    if (result->GetMaxInstances() == 0)
+    {
+      LOG(INFO) << "No limit on the number of C-FIND results at the Instance level";
+    }
+    else
+    {
+      LOG(INFO) << "Maximum " << result->GetMaxInstances() 
+                << " instances will be returned for C-FIND queries at the Instance level";
+    }
+
+    return result.release();
   }
 
   virtual IMoveRequestHandler* ConstructMoveRequestHandler()
--- a/Resources/Configuration.json	Wed Jun 25 09:06:34 2014 +0200
+++ b/Resources/Configuration.json	Wed Jun 25 11:08:11 2014 +0200
@@ -160,5 +160,14 @@
   // will be computed and stored in the Orthanc database. This
   // information can be used to detect disk corruption, at the price
   // of a small performance overhead.
-  "StoreMD5ForAttachments" : true
+  "StoreMD5ForAttachments" : true,
+
+  // The maximum number of results for a single C-FIND request at the
+  // Patient, Study or Series level. Setting this option to "0" means
+  // no limit.
+  "LimitFindResults" : 0,
+
+  // The maximum number of results for a single C-FIND request at the
+  // Instance level. Setting this option to "0" means no limit.
+  "LimitFindInstances" : 0
 }