comparison Plugins/Engine/OrthancPlugins.cpp @ 3414:b9cba6a91780

simplification
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 11 Jun 2019 19:44:10 +0200
parents f09bfdea3fc3
children 2a821deece64
comparison
equal deleted inserted replaced
3413:f09bfdea3fc3 3414:b9cba6a91780
49 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h" 49 #include "../../Core/DicomParsing/DicomWebJsonVisitor.h"
50 #include "../../Core/DicomParsing/FromDcmtkBridge.h" 50 #include "../../Core/DicomParsing/FromDcmtkBridge.h"
51 #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h" 51 #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h"
52 #include "../../Core/DicomParsing/ToDcmtkBridge.h" 52 #include "../../Core/DicomParsing/ToDcmtkBridge.h"
53 #include "../../Core/HttpServer/HttpToolbox.h" 53 #include "../../Core/HttpServer/HttpToolbox.h"
54 #include "../../Core/HttpServer/MultipartStreamReader.h"
55 #include "../../Core/Images/Image.h" 54 #include "../../Core/Images/Image.h"
56 #include "../../Core/Images/ImageProcessing.h" 55 #include "../../Core/Images/ImageProcessing.h"
57 #include "../../Core/Images/JpegReader.h" 56 #include "../../Core/Images/JpegReader.h"
58 #include "../../Core/Images/JpegWriter.h" 57 #include "../../Core/Images/JpegWriter.h"
59 #include "../../Core/Images/PngReader.h" 58 #include "../../Core/Images/PngReader.h"
74 73
75 #include <boost/regex.hpp> 74 #include <boost/regex.hpp>
76 #include <dcmtk/dcmdata/dcdict.h> 75 #include <dcmtk/dcmdata/dcdict.h>
77 #include <dcmtk/dcmdata/dcdicent.h> 76 #include <dcmtk/dcmdata/dcdicent.h>
78 77
78 #define ERROR_MESSAGE_64BIT "A 64bit version of the Orthanc API is necessary"
79
79 namespace Orthanc 80 namespace Orthanc
80 { 81 {
81 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, 82 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target,
82 const void* data, 83 const void* data,
83 size_t size) 84 size_t size)
84 { 85 {
86 if (static_cast<uint32_t>(size) != size)
87 {
88 throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
89 }
90
85 target.size = size; 91 target.size = size;
86 92
87 if (size == 0) 93 if (size == 0)
88 { 94 {
89 target.data = NULL; 95 target.data = NULL;
118 } 124 }
119 125
120 126
121 static char* CopyString(const std::string& str) 127 static char* CopyString(const std::string& str)
122 { 128 {
129 if (static_cast<uint32_t>(str.size()) != str.size())
130 {
131 throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
132 }
133
123 char *result = reinterpret_cast<char*>(malloc(str.size() + 1)); 134 char *result = reinterpret_cast<char*>(malloc(str.size() + 1));
124 if (result == NULL) 135 if (result == NULL)
125 { 136 {
126 throw OrthancException(ErrorCode_NotEnoughMemory); 137 throw OrthancException(ErrorCode_NotEnoughMemory);
127 } 138 }
537 } 548 }
538 } 549 }
539 }; 550 };
540 551
541 552
542 class MultipartRestCallback : public boost::noncopyable 553 class ChunkedRestCallback : public boost::noncopyable
543 { 554 {
544 private: 555 private:
545 _OrthancPluginMultipartRestCallback parameters_; 556 _OrthancPluginChunkedRestCallback parameters_;
546 boost::regex regex_; 557 boost::regex regex_;
547 558
548 public: 559 public:
549 MultipartRestCallback(_OrthancPluginMultipartRestCallback parameters) : 560 ChunkedRestCallback(_OrthancPluginChunkedRestCallback parameters) :
550 parameters_(parameters), 561 parameters_(parameters),
551 regex_(parameters.pathRegularExpression) 562 regex_(parameters.pathRegularExpression)
552 { 563 {
553 } 564 }
554 565
555 const boost::regex& GetRegularExpression() const 566 const boost::regex& GetRegularExpression() const
556 { 567 {
557 return regex_; 568 return regex_;
558 } 569 }
559 570
560 const _OrthancPluginMultipartRestCallback& GetParameters() const 571 const _OrthancPluginChunkedRestCallback& GetParameters() const
561 { 572 {
562 return parameters_; 573 return parameters_;
563 } 574 }
564 }; 575 };
565 576
596 } 607 }
597 608
598 609
599 typedef std::pair<std::string, _OrthancPluginProperty> Property; 610 typedef std::pair<std::string, _OrthancPluginProperty> Property;
600 typedef std::list<RestCallback*> RestCallbacks; 611 typedef std::list<RestCallback*> RestCallbacks;
601 typedef std::list<MultipartRestCallback*> MultipartRestCallbacks; 612 typedef std::list<ChunkedRestCallback*> ChunkedRestCallbacks;
602 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; 613 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks;
603 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; 614 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks;
604 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters; 615 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters;
605 typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2; 616 typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2;
606 typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks; 617 typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks;
609 typedef std::map<Property, std::string> Properties; 620 typedef std::map<Property, std::string> Properties;
610 621
611 PluginsManager manager_; 622 PluginsManager manager_;
612 623
613 RestCallbacks restCallbacks_; 624 RestCallbacks restCallbacks_;
614 MultipartRestCallbacks multipartRestCallbacks_; 625 ChunkedRestCallbacks chunkedRestCallbacks_;
615 OnStoredCallbacks onStoredCallbacks_; 626 OnStoredCallbacks onStoredCallbacks_;
616 OnChangeCallbacks onChangeCallbacks_; 627 OnChangeCallbacks onChangeCallbacks_;
617 OrthancPluginFindCallback findCallback_; 628 OrthancPluginFindCallback findCallback_;
618 OrthancPluginWorklistCallback worklistCallback_; 629 OrthancPluginWorklistCallback worklistCallback_;
619 DecodeImageCallbacks decodeImageCallbacks_; 630 DecodeImageCallbacks decodeImageCallbacks_;
1028 } 1039 }
1029 }; 1040 };
1030 1041
1031 1042
1032 1043
1033 class OrthancPlugins::ChunkedHttpRequest : public HttpClient::IRequestBody 1044 class OrthancPlugins::HttpClientChunkedRequest : public HttpClient::IRequestBody
1034 { 1045 {
1035 private: 1046 private:
1036 const _OrthancPluginChunkedHttpClient& params_; 1047 const _OrthancPluginChunkedHttpClient& params_;
1037 PluginsErrorDictionary& errorDictionary_; 1048 PluginsErrorDictionary& errorDictionary_;
1038 1049
1039 public: 1050 public:
1040 ChunkedHttpRequest(const _OrthancPluginChunkedHttpClient& params, 1051 HttpClientChunkedRequest(const _OrthancPluginChunkedHttpClient& params,
1041 PluginsErrorDictionary& errorDictionary) : 1052 PluginsErrorDictionary& errorDictionary) :
1042 params_(params), 1053 params_(params),
1043 errorDictionary_(errorDictionary) 1054 errorDictionary_(errorDictionary)
1044 { 1055 {
1045 } 1056 }
1046 1057
1076 } 1087 }
1077 } 1088 }
1078 }; 1089 };
1079 1090
1080 1091
1081 class OrthancPlugins::ChunkedHttpAnswer : public HttpClient::IAnswer 1092 class OrthancPlugins::HttpClientChunkedAnswer : public HttpClient::IAnswer
1082 { 1093 {
1083 private: 1094 private:
1084 const _OrthancPluginChunkedHttpClient& params_; 1095 const _OrthancPluginChunkedHttpClient& params_;
1085 PluginsErrorDictionary& errorDictionary_; 1096 PluginsErrorDictionary& errorDictionary_;
1086 1097
1087 public: 1098 public:
1088 ChunkedHttpAnswer(const _OrthancPluginChunkedHttpClient& params, 1099 HttpClientChunkedAnswer(const _OrthancPluginChunkedHttpClient& params,
1089 PluginsErrorDictionary& errorDictionary) : 1100 PluginsErrorDictionary& errorDictionary) :
1090 params_(params), 1101 params_(params),
1091 errorDictionary_(errorDictionary) 1102 errorDictionary_(errorDictionary)
1092 { 1103 {
1093 } 1104 }
1094 1105
1178 it != pimpl_->restCallbacks_.end(); ++it) 1189 it != pimpl_->restCallbacks_.end(); ++it)
1179 { 1190 {
1180 delete *it; 1191 delete *it;
1181 } 1192 }
1182 1193
1183 for (PImpl::MultipartRestCallbacks::iterator it = pimpl_->multipartRestCallbacks_.begin(); 1194 for (PImpl::ChunkedRestCallbacks::iterator it = pimpl_->chunkedRestCallbacks_.begin();
1184 it != pimpl_->multipartRestCallbacks_.end(); ++it) 1195 it != pimpl_->chunkedRestCallbacks_.end(); ++it)
1185 { 1196 {
1186 delete *it; 1197 delete *it;
1187 } 1198 }
1188 } 1199 }
1189 1200
1230 std::vector<std::string> groups_; 1241 std::vector<std::string> groups_;
1231 std::vector<const char*> cgroups_; 1242 std::vector<const char*> cgroups_;
1232 1243
1233 public: 1244 public:
1234 RestCallbackMatcher(const UriComponents& uri) : 1245 RestCallbackMatcher(const UriComponents& uri) :
1235 flatUri_(Toolbox::FlattenUri(uri)) 1246 flatUri_(Toolbox::FlattenUri(uri))
1236 { 1247 {
1237 } 1248 }
1238 1249
1239 bool IsMatch(const boost::regex& re) 1250 bool IsMatch(const boost::regex& re)
1240 { 1251 {
1278 const std::string& GetFlatUri() const 1289 const std::string& GetFlatUri() const
1279 { 1290 {
1280 return flatUri_; 1291 return flatUri_;
1281 } 1292 }
1282 }; 1293 };
1294
1295
1296 // WARNING - The lifetime of this internal object must be smaller
1297 // than "matcher", "headers" and "getArguments" objects
1298 class HttpRequestConverter
1299 {
1300 private:
1301 std::vector<const char*> getKeys_;
1302 std::vector<const char*> getValues_;
1303 std::vector<const char*> headersKeys_;
1304 std::vector<const char*> headersValues_;
1305 OrthancPluginHttpRequest converted_;
1306
1307 public:
1308 HttpRequestConverter(const RestCallbackMatcher& matcher,
1309 HttpMethod method,
1310 const IHttpHandler::Arguments& headers)
1311 {
1312 memset(&converted_, 0, sizeof(OrthancPluginHttpRequest));
1313
1314 ArgumentsToPlugin(headersKeys_, headersValues_, headers);
1315 assert(headersKeys_.size() == headersValues_.size());
1316
1317 switch (method)
1318 {
1319 case HttpMethod_Get:
1320 converted_.method = OrthancPluginHttpMethod_Get;
1321 break;
1322
1323 case HttpMethod_Post:
1324 converted_.method = OrthancPluginHttpMethod_Post;
1325 break;
1326
1327 case HttpMethod_Delete:
1328 converted_.method = OrthancPluginHttpMethod_Delete;
1329 break;
1330
1331 case HttpMethod_Put:
1332 converted_.method = OrthancPluginHttpMethod_Put;
1333 break;
1334
1335 default:
1336 throw OrthancException(ErrorCode_InternalError);
1337 }
1338
1339 converted_.groups = matcher.GetGroups();
1340 converted_.groupsCount = matcher.GetGroupsCount();
1341 converted_.getCount = 0;
1342 converted_.getKeys = NULL;
1343 converted_.getValues = NULL;
1344 converted_.body = NULL;
1345 converted_.bodySize = 0;
1346 converted_.headersCount = headers.size();
1347
1348 if (headers.size() > 0)
1349 {
1350 converted_.headersKeys = &headersKeys_[0];
1351 converted_.headersValues = &headersValues_[0];
1352 }
1353 }
1354
1355 void SetGetArguments(const IHttpHandler::GetArguments& getArguments)
1356 {
1357 ArgumentsToPlugin(getKeys_, getValues_, getArguments);
1358 assert(getKeys_.size() == getValues_.size());
1359
1360 converted_.getCount = getArguments.size();
1361
1362 if (getArguments.size() > 0)
1363 {
1364 converted_.getKeys = &getKeys_[0];
1365 converted_.getValues = &getValues_[0];
1366 }
1367 }
1368
1369 OrthancPluginHttpRequest& GetRequest()
1370 {
1371 return converted_;
1372 }
1373 };
1374 }
1375
1376
1377 bool OrthancPlugins::HandleChunkedGetDelete(HttpOutput& output,
1378 HttpMethod method,
1379 const UriComponents& uri,
1380 const Arguments& headers,
1381 const GetArguments& getArguments)
1382 {
1383 if (method == HttpMethod_Get ||
1384 method == HttpMethod_Delete)
1385 {
1386 RestCallbackMatcher matcher(uri);
1387
1388 PImpl::ChunkedRestCallback* callback = NULL;
1389
1390 // Loop over the callbacks registered by the plugins
1391 for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin();
1392 it != pimpl_->chunkedRestCallbacks_.end(); ++it)
1393 {
1394 if (matcher.IsMatch((*it)->GetRegularExpression()))
1395 {
1396 callback = *it;
1397 break;
1398 }
1399 }
1400
1401 if (callback != NULL)
1402 {
1403 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri();
1404
1405 HttpRequestConverter converter(matcher, method, headers);
1406 converter.SetGetArguments(getArguments);
1407
1408 PImpl::PluginHttpOutput pluginOutput(output);
1409
1410 assert(callback != NULL);
1411 OrthancPluginErrorCode error = callback->GetParameters().handler
1412 (reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput),
1413 NULL /* no reader */, matcher.GetFlatUri().c_str(), &converter.GetRequest());
1414
1415 pluginOutput.Close(error, GetErrorDictionary());
1416 return true;
1417 }
1418 }
1419
1420 return false;
1283 } 1421 }
1284 1422
1285 1423
1286 bool OrthancPlugins::Handle(HttpOutput& output, 1424 bool OrthancPlugins::Handle(HttpOutput& output,
1287 RequestOrigin /*origin*/, 1425 RequestOrigin /*origin*/,
1309 } 1447 }
1310 } 1448 }
1311 1449
1312 if (callback == NULL) 1450 if (callback == NULL)
1313 { 1451 {
1314 // Callback not found 1452 // Callback not found, try to find a chunked callback
1315 return false; 1453 return HandleChunkedGetDelete(output, method, uri, headers, getArguments);
1316 } 1454 }
1317 1455
1318 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri(); 1456 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri();
1319 1457
1320 std::vector<const char*> getKeys, getValues, headersKeys, headersValues; 1458 HttpRequestConverter converter(matcher, method, headers);
1321 1459 converter.SetGetArguments(getArguments);
1322 OrthancPluginHttpRequest request; 1460 converter.GetRequest().body = bodyData;
1323 memset(&request, 0, sizeof(OrthancPluginHttpRequest)); 1461 converter.GetRequest().bodySize = bodySize;
1324 1462
1325 ArgumentsToPlugin(headersKeys, headersValues, headers); 1463 PImpl::PluginHttpOutput pluginOutput(output);
1326 assert(headersKeys.size() == headersValues.size());
1327
1328 switch (method)
1329 {
1330 case HttpMethod_Get:
1331 request.method = OrthancPluginHttpMethod_Get;
1332 ArgumentsToPlugin(getKeys, getValues, getArguments);
1333 assert(getKeys.size() == getValues.size());
1334 break;
1335
1336 case HttpMethod_Post:
1337 request.method = OrthancPluginHttpMethod_Post;
1338 break;
1339
1340 case HttpMethod_Delete:
1341 request.method = OrthancPluginHttpMethod_Delete;
1342 break;
1343
1344 case HttpMethod_Put:
1345 request.method = OrthancPluginHttpMethod_Put;
1346 break;
1347
1348 default:
1349 throw OrthancException(ErrorCode_InternalError);
1350 }
1351
1352 request.groups = matcher.GetGroups();
1353 request.groupsCount = matcher.GetGroupsCount();
1354 request.getCount = getArguments.size();
1355 request.body = bodyData;
1356 request.bodySize = bodySize;
1357 request.headersCount = headers.size();
1358
1359 if (getArguments.size() > 0)
1360 {
1361 request.getKeys = &getKeys[0];
1362 request.getValues = &getValues[0];
1363 }
1364
1365 if (headers.size() > 0)
1366 {
1367 request.headersKeys = &headersKeys[0];
1368 request.headersValues = &headersValues[0];
1369 }
1370 1464
1371 assert(callback != NULL); 1465 assert(callback != NULL);
1372
1373 PImpl::PluginHttpOutput pluginOutput(output);
1374
1375 OrthancPluginErrorCode error = callback->Invoke 1466 OrthancPluginErrorCode error = callback->Invoke
1376 (pimpl_->restCallbackMutex_, pluginOutput, matcher.GetFlatUri(), request); 1467 (pimpl_->restCallbackMutex_, pluginOutput, matcher.GetFlatUri(), converter.GetRequest());
1377 1468
1378 pluginOutput.Close(error, GetErrorDictionary()); 1469 pluginOutput.Close(error, GetErrorDictionary());
1379 return true; 1470 return true;
1380 } 1471 }
1381 1472
1448 1539
1449 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock)); 1540 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock));
1450 } 1541 }
1451 1542
1452 1543
1453 void OrthancPlugins::RegisterMultipartRestCallback(const void* parameters) 1544 void OrthancPlugins::RegisterChunkedRestCallback(const void* parameters)
1454 { 1545 {
1455 const _OrthancPluginMultipartRestCallback& p = 1546 const _OrthancPluginChunkedRestCallback& p =
1456 *reinterpret_cast<const _OrthancPluginMultipartRestCallback*>(parameters); 1547 *reinterpret_cast<const _OrthancPluginChunkedRestCallback*>(parameters);
1457 1548
1458 LOG(INFO) << "Plugin has registered a REST callback for multipart streams on: " 1549 LOG(INFO) << "Plugin has registered a REST callback for chunked streams on: "
1459 << p.pathRegularExpression; 1550 << p.pathRegularExpression;
1460 1551
1461 pimpl_->multipartRestCallbacks_.push_back(new PImpl::MultipartRestCallback(p)); 1552 pimpl_->chunkedRestCallbacks_.push_back(new PImpl::ChunkedRestCallback(p));
1462 } 1553 }
1463 1554
1464 1555
1465 void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters) 1556 void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters)
1466 { 1557 {
2430 converted.pkcs11 = p.pkcs11; 2521 converted.pkcs11 = p.pkcs11;
2431 2522
2432 SetupHttpClient(client, converted); 2523 SetupHttpClient(client, converted);
2433 } 2524 }
2434 2525
2435 ChunkedHttpRequest body(p, pimpl_->dictionary_); 2526 HttpClientChunkedRequest body(p, pimpl_->dictionary_);
2436 client.SetBody(body); 2527 client.SetBody(body);
2437 2528
2438 ChunkedHttpAnswer answer(p, pimpl_->dictionary_); 2529 HttpClientChunkedAnswer answer(p, pimpl_->dictionary_);
2439 2530
2440 bool success = client.Apply(answer); 2531 bool success = client.Apply(answer);
2441 2532
2442 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus()); 2533 *p.httpStatus = static_cast<uint16_t>(client.GetLastStatus());
2443 2534
3544 3635
3545 case _OrthancPluginService_RegisterRestCallbackNoLock: 3636 case _OrthancPluginService_RegisterRestCallbackNoLock:
3546 RegisterRestCallback(parameters, false); 3637 RegisterRestCallback(parameters, false);
3547 return true; 3638 return true;
3548 3639
3549 case _OrthancPluginService_RegisterMultipartRestCallback: 3640 case _OrthancPluginService_RegisterChunkedRestCallback:
3550 RegisterMultipartRestCallback(parameters); 3641 RegisterChunkedRestCallback(parameters);
3551 return true; 3642 return true;
3552 3643
3553 case _OrthancPluginService_RegisterOnStoredInstanceCallback: 3644 case _OrthancPluginService_RegisterOnStoredInstanceCallback:
3554 RegisterOnStoredInstanceCallback(parameters); 3645 RegisterOnStoredInstanceCallback(parameters);
3555 return true; 3646 return true;
4108 } 4199 }
4109 } 4200 }
4110 } 4201 }
4111 4202
4112 4203
4113 class OrthancPlugins::MultipartStream : 4204 class OrthancPlugins::HttpServerChunkedReader : public IHttpHandler::IChunkedRequestReader
4114 public IHttpHandler::IStream,
4115 private MultipartStreamReader::IHandler
4116 { 4205 {
4117 private: 4206 private:
4118 OrthancPluginMultipartRestHandler* handler_; 4207 OrthancPluginServerChunkedRequestReader* reader_;
4119 _OrthancPluginMultipartRestCallback parameters_; 4208 _OrthancPluginChunkedRestCallback parameters_;
4120 MultipartStreamReader reader_; 4209 PluginsErrorDictionary& errorDictionary_;
4121 PluginsErrorDictionary& errorDictionary_;
4122
4123 virtual void HandlePart(const MultipartStreamReader::HttpHeaders& headers,
4124 const void* part,
4125 size_t size)
4126 {
4127 assert(handler_ != NULL);
4128
4129 std::string contentType;
4130 MultipartStreamReader::GetMainContentType(contentType, headers);
4131 Orthanc::Toolbox::ToLowerCase(contentType);
4132
4133 std::vector<const char*> headersKeys, headersValues;
4134 ArgumentsToPlugin(headersKeys, headersValues, headers);
4135 assert(headersKeys.size() == headersValues.size());
4136
4137 OrthancPluginErrorCode error = parameters_.addPart(
4138 handler_, contentType.c_str(), headersKeys.size(),
4139 headersKeys.empty() ? NULL : &headersKeys[0],
4140 headersValues.empty() ? NULL : &headersValues[0],
4141 part, size);
4142
4143 if (error != OrthancPluginErrorCode_Success)
4144 {
4145 errorDictionary_.LogError(error, true);
4146 throw OrthancException(static_cast<ErrorCode>(error));
4147 }
4148 }
4149 4210
4150 public: 4211 public:
4151 MultipartStream(OrthancPluginMultipartRestHandler* handler, 4212 HttpServerChunkedReader(OrthancPluginServerChunkedRequestReader* reader,
4152 const _OrthancPluginMultipartRestCallback& parameters, 4213 const _OrthancPluginChunkedRestCallback& parameters,
4153 const std::string& boundary, 4214 PluginsErrorDictionary& errorDictionary) :
4154 PluginsErrorDictionary& errorDictionary) : 4215 reader_(reader),
4155 handler_(handler),
4156 parameters_(parameters), 4216 parameters_(parameters),
4157 reader_(boundary),
4158 errorDictionary_(errorDictionary) 4217 errorDictionary_(errorDictionary)
4159 { 4218 {
4160 if (handler_ == NULL) 4219 assert(reader_ != NULL);
4161 { 4220 }
4162 throw OrthancException(ErrorCode_Plugin, "The plugin has not created a multipart stream handler"); 4221
4163 } 4222 virtual ~HttpServerChunkedReader()
4164 4223 {
4165 reader_.SetHandler(*this); 4224 assert(reader_ != NULL);
4166 } 4225 parameters_.finalize(reader_);
4167
4168 virtual ~MultipartStream()
4169 {
4170 if (handler_ != NULL)
4171 {
4172 parameters_.finalize(handler_);
4173 }
4174 } 4226 }
4175 4227
4176 virtual void AddBodyChunk(const void* data, 4228 virtual void AddBodyChunk(const void* data,
4177 size_t size) 4229 size_t size)
4178 { 4230 {
4179 reader_.AddChunk(data, size); 4231 if (static_cast<uint32_t>(size) != size)
4180 } 4232 {
4233 throw OrthancException(ErrorCode_NotEnoughMemory, ERROR_MESSAGE_64BIT);
4234 }
4235
4236 assert(reader_ != NULL);
4237 parameters_.addChunk(reader_, data, size);
4238 }
4181 4239
4182 virtual void Execute(HttpOutput& output) 4240 virtual void Execute(HttpOutput& output)
4183 { 4241 {
4184 assert(handler_ != NULL); 4242 assert(reader_ != NULL);
4185
4186 reader_.CloseStream();
4187 4243
4188 PImpl::PluginHttpOutput pluginOutput(output); 4244 PImpl::PluginHttpOutput pluginOutput(output);
4189 4245
4190 OrthancPluginErrorCode error = parameters_.execute( 4246 OrthancPluginErrorCode error = parameters_.execute(
4191 handler_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput)); 4247 reader_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput));
4192 4248
4193 pluginOutput.Close(error, errorDictionary_); 4249 pluginOutput.Close(error, errorDictionary_);
4194 } 4250 }
4195 }; 4251 };
4196 4252
4197 4253
4198 IHttpHandler::IStream* OrthancPlugins::CreateStreamHandler(RequestOrigin origin, 4254 bool OrthancPlugins::CreateChunkedRequestReader(std::auto_ptr<IChunkedRequestReader>& target,
4199 const char* remoteIp, 4255 RequestOrigin origin,
4200 const char* username, 4256 const char* remoteIp,
4201 HttpMethod method, 4257 const char* username,
4202 const UriComponents& uri, 4258 HttpMethod method,
4203 const Arguments& headers) 4259 const UriComponents& uri,
4204 { 4260 const Arguments& headers)
4261 {
4262 if (method != HttpMethod_Post &&
4263 method != HttpMethod_Put)
4264 {
4265 throw OrthancException(ErrorCode_InternalError);
4266 }
4267
4205 RestCallbackMatcher matcher(uri); 4268 RestCallbackMatcher matcher(uri);
4206 4269
4270 PImpl::ChunkedRestCallback* callback = NULL;
4271
4207 // Loop over the callbacks registered by the plugins 4272 // Loop over the callbacks registered by the plugins
4208 for (PImpl::MultipartRestCallbacks::const_iterator it = pimpl_->multipartRestCallbacks_.begin(); 4273 for (PImpl::ChunkedRestCallbacks::const_iterator it = pimpl_->chunkedRestCallbacks_.begin();
4209 it != pimpl_->multipartRestCallbacks_.end(); ++it) 4274 it != pimpl_->chunkedRestCallbacks_.end(); ++it)
4210 { 4275 {
4211 if (matcher.IsMatch((*it)->GetRegularExpression())) 4276 if (matcher.IsMatch((*it)->GetRegularExpression()))
4212 { 4277 {
4213 LOG(INFO) << "Delegating HTTP multipart request to plugin for URI: " << matcher.GetFlatUri(); 4278 callback = *it;
4214 4279 break;
4215 std::vector<const char*> headersKeys, headersValues; 4280 }
4216 ArgumentsToPlugin(headersKeys, headersValues, headers); 4281 }
4217 assert(headersKeys.size() == headersValues.size()); 4282
4218 4283 if (callback == NULL)
4219 OrthancPluginHttpMethod convertedMethod; 4284 {
4220 switch (method) 4285 // Callback not found
4221 { 4286 return false;
4222 case HttpMethod_Post: 4287 }
4223 convertedMethod = OrthancPluginHttpMethod_Post; 4288
4224 break; 4289 LOG(INFO) << "Delegating chunked HTTP request to plugin for URI: " << matcher.GetFlatUri();
4225 4290
4226 case HttpMethod_Put: 4291 HttpRequestConverter converter(matcher, method, headers);
4227 convertedMethod = OrthancPluginHttpMethod_Put; 4292 converter.GetRequest().body = NULL;
4228 break; 4293 converter.GetRequest().bodySize = 0;
4229 4294
4230 default: 4295 OrthancPluginServerChunkedRequestReader* reader = NULL;
4231 throw OrthancException(ErrorCode_ParameterOutOfRange); 4296
4232 } 4297 OrthancPluginErrorCode errorCode = callback->GetParameters().handler(
4233 4298 NULL /* no HTTP output */, &reader, matcher.GetFlatUri().c_str(), &converter.GetRequest());
4234 std::string mainContentType; 4299
4235 if (!MultipartStreamReader::GetMainContentType(mainContentType, headers)) 4300 if (reader == NULL)
4236 { 4301 {
4237 LOG(INFO) << "Missing Content-Type HTTP header, prevents streaming the body"; 4302 // The plugin has not created a reader for chunked body
4238 continue; 4303 return false;
4239 } 4304 }
4240 4305 else if (errorCode != OrthancPluginErrorCode_Success)
4241 std::string contentType, subType, boundary; 4306 {
4242 if (!MultipartStreamReader::ParseMultipartContentType 4307 throw OrthancException(static_cast<ErrorCode>(errorCode));
4243 (contentType, subType, boundary, mainContentType)) 4308 }
4244 { 4309 else
4245 LOG(INFO) << "Invalid Content-Type HTTP header, " 4310 {
4246 << "prevents streaming the body: \"" << mainContentType << "\""; 4311 target.reset(new HttpServerChunkedReader(reader, callback->GetParameters(), GetErrorDictionary()));
4247 continue; 4312 return true;
4248 } 4313 }
4249
4250 OrthancPluginErrorCode errorCode = OrthancPluginErrorCode_Plugin;
4251
4252 OrthancPluginMultipartRestHandler* handler = (*it)->GetParameters().createHandler(
4253 (*it)->GetParameters().factory, &errorCode,
4254 convertedMethod, matcher.GetFlatUri().c_str(), contentType.c_str(), subType.c_str(),
4255 matcher.GetGroupsCount(), matcher.GetGroups(), headers.size(),
4256 headers.empty() ? NULL : &headersKeys[0],
4257 headers.empty() ? NULL : &headersValues[0]);
4258
4259 if (handler == NULL)
4260 {
4261 if (errorCode == OrthancPluginErrorCode_Success)
4262 {
4263 // Ignore: The factory cannot create a handler for this request
4264 }
4265 else
4266 {
4267 throw OrthancException(static_cast<ErrorCode>(errorCode));
4268 }
4269 }
4270 else
4271 {
4272 return new MultipartStream(handler, (*it)->GetParameters(), boundary, GetErrorDictionary());
4273 }
4274 }
4275 }
4276
4277 return NULL;
4278 } 4314 }
4279 } 4315 }