changeset 106:100c20770a25 dev

fixes in STOW-RS
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 27 Apr 2016 13:45:08 +0200
parents e1e2b6b2139d
children fea72403194b
files AUTHORS Plugin/Configuration.cpp Plugin/Plugin.cpp README Resources/Samples/JavaScript/index.html Resources/Samples/SendStow.py
diffstat 6 files changed, 79 insertions(+), 64 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Tue Apr 26 17:38:47 2016 +0200
+++ b/AUTHORS	Wed Apr 27 13:45:08 2016 +0200
@@ -1,5 +1,5 @@
-DICOM Web plugin for Orthanc
-============================
+DICOMweb plugin for Orthanc
+===========================
 
 
 Authors
--- a/Plugin/Configuration.cpp	Tue Apr 26 17:38:47 2016 +0200
+++ b/Plugin/Configuration.cpp	Wed Apr 27 13:45:08 2016 +0200
@@ -92,7 +92,7 @@
 
     result.clear();
 
-    const boost::regex separator("\r\n--" + boundary + "(--|\r\n)");
+    const boost::regex separator("(^|\r\n)--" + boundary + "(--|\r\n)");
     const boost::regex encapsulation("(.*)\r\n\r\n(.*)");
  
     std::vector< std::pair<const char*, const char*> > parts;
@@ -101,7 +101,7 @@
     const char* end = body + bodySize;
 
     boost::cmatch what;
-    boost::match_flag_type flags = boost::match_perl;
+    boost::match_flag_type flags = boost::match_perl | boost::match_single_line;
     while (boost::regex_search(start, end, what, separator, flags))   
     {
       if (start != body)  // Ignore the first separator
@@ -109,7 +109,7 @@
         parts.push_back(std::make_pair(start, what[0].first));
       }
 
-      if (*what[1].first == '-')
+      if (*what[2].first == '-')
       {
         // This is the last separator (there is a trailing "--")
         break;
@@ -119,7 +119,6 @@
       flags |= boost::match_prev_avail;
     }
 
-
     for (size_t i = 0; i < parts.size(); i++)
     {
       if (boost::regex_match(parts[i].first, parts[i].second, what, encapsulation, boost::match_perl))
--- a/Plugin/Plugin.cpp	Tue Apr 26 17:38:47 2016 +0200
+++ b/Plugin/Plugin.cpp	Wed Apr 27 13:45:08 2016 +0200
@@ -287,24 +287,9 @@
 }
 
 
-
-void StowClient(OrthancPluginRestOutput* output,
-                const char* url,
-                const OrthancPluginHttpRequest* request)
+static void GetListOfInstances(std::list<std::string>& instances,
+                               const OrthancPluginHttpRequest* request)
 {
-  if (request->groupsCount != 1)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
-  }
-
-  if (request->method != OrthancPluginHttpMethod_Post)
-  {
-    OrthancPluginSendMethodNotAllowed(context_, output, "POST");
-    return;
-  }
-
-  std::string peer(request->groups[0]);
-
   Json::Value resources;
   Json::Reader reader;
   if (!reader.parse(request->body, request->body + request->bodySize, resources) ||
@@ -316,7 +301,6 @@
   }
 
   // Extract information about all the child instances
-  std::list<std::string> instances;
   for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++)
   {
     if (resources[i].type() != Json::stringValue)
@@ -354,8 +338,42 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
     }   
   }
+}
 
 
+
+void StowClient(OrthancPluginRestOutput* output,
+                const char* /*url*/,
+                const OrthancPluginHttpRequest* request)
+{
+  if (request->groupsCount != 1)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest);
+  }
+
+  if (request->method != OrthancPluginHttpMethod_Post)
+  {
+    OrthancPluginSendMethodNotAllowed(context_, output, "POST");
+    return;
+  }
+
+  {
+    std::string peer(request->groups[0]);
+    // TODO
+  }
+
+  std::string url = "http://localhost:8043/dicom-web/studies";
+
+
+  std::list<std::string> instances;
+  GetListOfInstances(instances, request);
+
+  {
+    std::string s = ("Sending " + boost::lexical_cast<std::string>(instances.size()) + 
+                     " instances using STOW-RS to DICOMweb server: " + url);
+    OrthancPluginLogInfo(context_, s.c_str());
+  }
+
   std::string boundary;
 
   {
@@ -376,30 +394,28 @@
   std::string mime = "multipart/related; type=application/dicom; boundary=" + boundary;
 
   Orthanc::ChunkedBuffer chunks;
-  chunks.AddChunk("\r\n"); // Empty preamble
 
   for (std::list<std::string>::const_iterator it = instances.begin(); it != instances.end(); it++)
   {
     std::string dicom;
     if (OrthancPlugins::RestApiGetString(dicom, context_, "/instances/" + *it + "/file"))
     {
-      chunks.AddChunk("--" + boundary + "\r\n" +
+      chunks.AddChunk("\r\n--" + boundary + "\r\n" +
                       "Content-Type: application/dicom\r\n" +
                       "Content-Length: " + boost::lexical_cast<std::string>(dicom.size()) +
                       "\r\n\r\n");
       chunks.AddChunk(dicom);
-      chunks.AddChunk("\r\n");
     }
   }
 
-  chunks.AddChunk("--" + boundary + "--\r\n");
+  chunks.AddChunk("\r\n--" + boundary + "--\r\n");
 
   std::string body;
   chunks.Flatten(body);
 
   // TODO Split the message
 
-  SendStowRequest("http://localhost:8043/dicom-web/studies", NULL, NULL, body, mime, instances.size());
+  SendStowRequest(url, NULL, NULL, body, mime, instances.size());
 }
 
 
@@ -422,17 +438,7 @@
       return -1;
     }
 
-    {
-      std::string version(context_->orthancVersion);
-      if (version == "0.9.1")
-      {
-        OrthancPluginLogWarning(context_, "If using STOW-RS, the DICOMweb plugin can lead to "
-                                "deadlocks in Orthanc version 0.9.1. Please upgrade Orthanc!");
-      }
-    }
-
-
-    OrthancPluginSetDescription(context_, "Implementation of DICOM Web (QIDO-RS, STOW-RS and WADO-RS) and WADO.");
+    OrthancPluginSetDescription(context_, "Implementation of DICOMweb (QIDO-RS, STOW-RS and WADO-RS) and WADO-URI.");
 
     // Read the configuration
     dictionary_ = &gdcm::Global::GetInstance().GetDicts().GetPublicDict();
--- a/README	Tue Apr 26 17:38:47 2016 +0200
+++ b/README	Wed Apr 27 13:45:08 2016 +0200
@@ -1,5 +1,5 @@
-DICOM Web plugin for Orthanc
-============================
+DICOMweb plugin for Orthanc
+===========================
 
 
 General Information
@@ -10,8 +10,8 @@
 extends the RESTful API of Orthanc with WADO and DICOMweb support.
 
 
-DICOM Web Support
------------------
+DICOMweb Support
+----------------
 
 Currently, a basic support of the following protocols is provided:
 
@@ -41,14 +41,14 @@
 Samples
 -------
 
-Python samples to call the DICOM Web services can be found in the
+Python samples to call the DICOMweb services can be found in the
 "./Samples" folder.
 
 
 Licensing: AGPL
 ---------------
 
-The DICOM Web plugin for Orthanc is licensed under the Affero General
+The DICOMweb plugin for Orthanc is licensed under the Affero General
 Public License (AGPL) license. Pay attention to the fact that this
 license is more restrictive than the license of the Orthanc core.
 
--- a/Resources/Samples/JavaScript/index.html	Tue Apr 26 17:38:47 2016 +0200
+++ b/Resources/Samples/JavaScript/index.html	Wed Apr 27 13:45:08 2016 +0200
@@ -3,11 +3,11 @@
 <html lang="us">
   <head>
     <meta charset="utf-8" />
-    <title>Orthanc DICOM Web Demo</title>
+    <title>Orthanc DICOMweb Demo</title>
   </head>
 
   <body>
-    <h1>Orthanc DICOM Web Demo</h2>
+    <h1>Orthanc DICOMweb Demo</h2>
 
     <h2>STOW-RS - Upload DICOM file</h2>
     <form id="stow">
--- a/Resources/Samples/SendStow.py	Tue Apr 26 17:38:47 2016 +0200
+++ b/Resources/Samples/SendStow.py	Wed Apr 27 13:45:08 2016 +0200
@@ -18,12 +18,17 @@
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
 
-import email
+
+# We do not use Python's "email" package, as it uses LF (\n) for line
+# endings instead of CRLF (\r\n) for binary messages, as required by
+# RFC 1341
+# http://stackoverflow.com/questions/3086860/how-do-i-generate-a-multipart-mime-message-with-correct-crlf-in-python
+# https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
+
 import requests
 import sys
 import json
-from email.mime.multipart import MIMEMultipart
-from email.mime.application import MIMEApplication
+import uuid
 
 if len(sys.argv) < 2:
     print('Usage: %s <StowUri> <file>...' % sys.argv[0])
@@ -33,27 +38,29 @@
 
 URL = sys.argv[1]
 
-related = MIMEMultipart('related')
-related.set_boundary('hello')
+# Create a multipart message whose body contains all the input DICOM files
+boundary = str(uuid.uuid4())  # The boundary is a random UUID
+body = bytearray()
 
 for i in range(2, len(sys.argv)):
     try:
         with open(sys.argv[i], 'rb') as f:
-            dicom = MIMEApplication(f.read(), 'dicom', email.encoders.encode_noop)
-            related.attach(dicom)
+            body += bytearray('--%s\r\n' % boundary, 'ascii')
+            body += bytearray('Content-Type: application/dicom\r\n\r\n', 'ascii')
+            body += f.read()
+            body += bytearray('\r\n', 'ascii')
     except:
         print('Ignoring directory %s' % sys.argv[i])
 
-headers = dict(related.items())
-body = related.as_string()
+# Closing boundary
+body += bytearray('--%s--' % boundary, 'ascii')
 
-# Discard the header
-body = body.split('\n\n', 1)[1]
+# Do the HTTP POST request to the STOW-RS server
+r = requests.post(URL, data=body, headers= {
+    'Content-Type' : 'multipart/related; type=application/dicom; boundary=%s' % boundary,
+    'Accept' : 'application/json',
+})
 
-headers['Content-Type'] = 'multipart/related; type=application/dicom; boundary=%s' % related.get_boundary()
-headers['Accept'] = 'application/json'
-
-r = requests.post(URL, data=body, headers=headers)
 j = json.loads(r.text)
 
 # Loop over the successful instances
@@ -64,4 +71,7 @@
         print(url)
 
 print('\nWADO-RS URL of the study:')
-print(j['00081190']['Value'][0])
+try:
+    print(j['00081190']['Value'][0])
+except:
+    print('No instance was uploaded!')