Mercurial > hg > orthanc
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 } |