Mercurial > hg > orthanc-dicomweb
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!')