Mercurial > hg > orthanc
comparison Plugins/Engine/OrthancPlugins.cpp @ 3396:4981405e6c5c
new sdk: OrthancPluginRegisterMultipartRestCallback()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 06 Jun 2019 21:35:05 +0200 |
parents | 0ce9b4f5fdf5 |
children | 9019279dbfd7 |
comparison
equal
deleted
inserted
replaced
3395:0ce9b4f5fdf5 | 3396:4981405e6c5c |
---|---|
457 else | 457 else |
458 { | 458 { |
459 return *errorDetails_; | 459 return *errorDetails_; |
460 } | 460 } |
461 } | 461 } |
462 | |
463 void Close(OrthancPluginErrorCode error, | |
464 PluginsErrorDictionary& dictionary) | |
465 { | |
466 if (error == OrthancPluginErrorCode_Success) | |
467 { | |
468 if (GetOutput().IsWritingMultipart()) | |
469 { | |
470 GetOutput().CloseMultipart(); | |
471 } | |
472 } | |
473 else | |
474 { | |
475 dictionary.LogError(error, false); | |
476 | |
477 if (HasErrorDetails()) | |
478 { | |
479 throw OrthancException(static_cast<ErrorCode>(error), | |
480 GetErrorDetails(), | |
481 IsLogDetails()); | |
482 } | |
483 else | |
484 { | |
485 throw OrthancException(static_cast<ErrorCode>(error)); | |
486 } | |
487 } | |
488 } | |
462 }; | 489 }; |
463 | 490 |
464 | 491 |
465 class RestCallback : public boost::noncopyable | 492 class RestCallback : public boost::noncopyable |
466 { | 493 { |
509 } | 536 } |
510 } | 537 } |
511 }; | 538 }; |
512 | 539 |
513 | 540 |
541 class MultipartRestCallback : public boost::noncopyable | |
542 { | |
543 private: | |
544 _OrthancPluginMultipartRestCallback parameters_; | |
545 boost::regex regex_; | |
546 | |
547 public: | |
548 MultipartRestCallback(_OrthancPluginMultipartRestCallback parameters) : | |
549 parameters_(parameters), | |
550 regex_(parameters.pathRegularExpression) | |
551 { | |
552 } | |
553 | |
554 const boost::regex& GetRegularExpression() const | |
555 { | |
556 return regex_; | |
557 } | |
558 | |
559 const _OrthancPluginMultipartRestCallback& GetParameters() const | |
560 { | |
561 return parameters_; | |
562 } | |
563 }; | |
564 | |
565 | |
514 class ServerContextLock | 566 class ServerContextLock |
515 { | 567 { |
516 private: | 568 private: |
517 boost::mutex::scoped_lock lock_; | 569 boost::mutex::scoped_lock lock_; |
518 ServerContext* context_; | 570 ServerContext* context_; |
543 } | 595 } |
544 | 596 |
545 | 597 |
546 typedef std::pair<std::string, _OrthancPluginProperty> Property; | 598 typedef std::pair<std::string, _OrthancPluginProperty> Property; |
547 typedef std::list<RestCallback*> RestCallbacks; | 599 typedef std::list<RestCallback*> RestCallbacks; |
600 typedef std::list<MultipartRestCallback*> MultipartRestCallbacks; | |
548 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; | 601 typedef std::list<OrthancPluginOnStoredInstanceCallback> OnStoredCallbacks; |
549 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; | 602 typedef std::list<OrthancPluginOnChangeCallback> OnChangeCallbacks; |
550 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters; | 603 typedef std::list<OrthancPluginIncomingHttpRequestFilter> IncomingHttpRequestFilters; |
551 typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2; | 604 typedef std::list<OrthancPluginIncomingHttpRequestFilter2> IncomingHttpRequestFilters2; |
552 typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks; | 605 typedef std::list<OrthancPluginDecodeImageCallback> DecodeImageCallbacks; |
555 typedef std::map<Property, std::string> Properties; | 608 typedef std::map<Property, std::string> Properties; |
556 | 609 |
557 PluginsManager manager_; | 610 PluginsManager manager_; |
558 | 611 |
559 RestCallbacks restCallbacks_; | 612 RestCallbacks restCallbacks_; |
613 MultipartRestCallbacks multipartRestCallbacks_; | |
560 OnStoredCallbacks onStoredCallbacks_; | 614 OnStoredCallbacks onStoredCallbacks_; |
561 OnChangeCallbacks onChangeCallbacks_; | 615 OnChangeCallbacks onChangeCallbacks_; |
562 OrthancPluginFindCallback findCallback_; | 616 OrthancPluginFindCallback findCallback_; |
563 OrthancPluginWorklistCallback worklistCallback_; | 617 OrthancPluginWorklistCallback worklistCallback_; |
564 DecodeImageCallbacks decodeImageCallbacks_; | 618 DecodeImageCallbacks decodeImageCallbacks_; |
1122 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); | 1176 for (PImpl::RestCallbacks::iterator it = pimpl_->restCallbacks_.begin(); |
1123 it != pimpl_->restCallbacks_.end(); ++it) | 1177 it != pimpl_->restCallbacks_.end(); ++it) |
1124 { | 1178 { |
1125 delete *it; | 1179 delete *it; |
1126 } | 1180 } |
1181 | |
1182 for (PImpl::MultipartRestCallbacks::iterator it = pimpl_->multipartRestCallbacks_.begin(); | |
1183 it != pimpl_->multipartRestCallbacks_.end(); ++it) | |
1184 { | |
1185 delete *it; | |
1186 } | |
1127 } | 1187 } |
1128 | 1188 |
1129 | 1189 |
1130 static void ArgumentsToPlugin(std::vector<const char*>& keys, | 1190 static void ArgumentsToPlugin(std::vector<const char*>& keys, |
1131 std::vector<const char*>& values, | 1191 std::vector<const char*>& values, |
1155 for (size_t i = 0; i < arguments.size(); i++) | 1215 for (size_t i = 0; i < arguments.size(); i++) |
1156 { | 1216 { |
1157 keys[i] = arguments[i].first.c_str(); | 1217 keys[i] = arguments[i].first.c_str(); |
1158 values[i] = arguments[i].second.c_str(); | 1218 values[i] = arguments[i].second.c_str(); |
1159 } | 1219 } |
1220 } | |
1221 | |
1222 | |
1223 namespace | |
1224 { | |
1225 class RestCallbackMatcher : public boost::noncopyable | |
1226 { | |
1227 private: | |
1228 std::string flatUri_; | |
1229 std::vector<std::string> groups_; | |
1230 std::vector<const char*> cgroups_; | |
1231 | |
1232 public: | |
1233 RestCallbackMatcher(const UriComponents& uri) : | |
1234 flatUri_(Toolbox::FlattenUri(uri)) | |
1235 { | |
1236 } | |
1237 | |
1238 bool IsMatch(const boost::regex& re) | |
1239 { | |
1240 // Check whether the regular expression associated to this | |
1241 // callback matches the URI | |
1242 boost::cmatch what; | |
1243 | |
1244 if (boost::regex_match(flatUri_.c_str(), what, re)) | |
1245 { | |
1246 // Extract the value of the free parameters of the regular expression | |
1247 if (what.size() > 1) | |
1248 { | |
1249 groups_.resize(what.size() - 1); | |
1250 cgroups_.resize(what.size() - 1); | |
1251 for (size_t i = 1; i < what.size(); i++) | |
1252 { | |
1253 groups_[i - 1] = what[i]; | |
1254 cgroups_[i - 1] = groups_[i - 1].c_str(); | |
1255 } | |
1256 } | |
1257 | |
1258 return true; | |
1259 } | |
1260 else | |
1261 { | |
1262 // Not a match | |
1263 return false; | |
1264 } | |
1265 } | |
1266 | |
1267 uint32_t GetGroupsCount() const | |
1268 { | |
1269 return cgroups_.size(); | |
1270 } | |
1271 | |
1272 const char* const* GetGroups() const | |
1273 { | |
1274 return cgroups_.empty() ? NULL : &cgroups_[0]; | |
1275 } | |
1276 | |
1277 const std::string& GetFlatUri() const | |
1278 { | |
1279 return flatUri_; | |
1280 } | |
1281 }; | |
1160 } | 1282 } |
1161 | 1283 |
1162 | 1284 |
1163 bool OrthancPlugins::Handle(HttpOutput& output, | 1285 bool OrthancPlugins::Handle(HttpOutput& output, |
1164 RequestOrigin /*origin*/, | 1286 RequestOrigin /*origin*/, |
1169 const Arguments& headers, | 1291 const Arguments& headers, |
1170 const GetArguments& getArguments, | 1292 const GetArguments& getArguments, |
1171 const char* bodyData, | 1293 const char* bodyData, |
1172 size_t bodySize) | 1294 size_t bodySize) |
1173 { | 1295 { |
1174 std::string flatUri = Toolbox::FlattenUri(uri); | 1296 RestCallbackMatcher matcher(uri); |
1297 | |
1175 PImpl::RestCallback* callback = NULL; | 1298 PImpl::RestCallback* callback = NULL; |
1176 | 1299 |
1177 std::vector<std::string> groups; | |
1178 std::vector<const char*> cgroups; | |
1179 | |
1180 // Loop over the callbacks registered by the plugins | 1300 // Loop over the callbacks registered by the plugins |
1181 bool found = false; | |
1182 for (PImpl::RestCallbacks::const_iterator it = pimpl_->restCallbacks_.begin(); | 1301 for (PImpl::RestCallbacks::const_iterator it = pimpl_->restCallbacks_.begin(); |
1183 it != pimpl_->restCallbacks_.end() && !found; ++it) | 1302 it != pimpl_->restCallbacks_.end(); ++it) |
1184 { | 1303 { |
1185 // Check whether the regular expression associated to this | 1304 if (matcher.IsMatch((*it)->GetRegularExpression())) |
1186 // callback matches the URI | |
1187 boost::cmatch what; | |
1188 if (boost::regex_match(flatUri.c_str(), what, (*it)->GetRegularExpression())) | |
1189 { | 1305 { |
1190 callback = *it; | 1306 callback = *it; |
1191 | 1307 break; |
1192 // Extract the value of the free parameters of the regular expression | |
1193 if (what.size() > 1) | |
1194 { | |
1195 groups.resize(what.size() - 1); | |
1196 cgroups.resize(what.size() - 1); | |
1197 for (size_t i = 1; i < what.size(); i++) | |
1198 { | |
1199 groups[i - 1] = what[i]; | |
1200 cgroups[i - 1] = groups[i - 1].c_str(); | |
1201 } | |
1202 } | |
1203 } | 1308 } |
1204 } | 1309 } |
1205 | 1310 |
1206 if (callback == NULL) | 1311 if (callback == NULL) |
1207 { | 1312 { |
1208 // Callback not found | 1313 // Callback not found |
1209 return false; | 1314 return false; |
1210 } | 1315 } |
1211 | 1316 |
1212 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << flatUri; | 1317 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << matcher.GetFlatUri(); |
1213 | 1318 |
1214 std::vector<const char*> getKeys, getValues, headersKeys, headersValues; | 1319 std::vector<const char*> getKeys, getValues, headersKeys, headersValues; |
1215 | 1320 |
1216 OrthancPluginHttpRequest request; | 1321 OrthancPluginHttpRequest request; |
1217 memset(&request, 0, sizeof(OrthancPluginHttpRequest)); | 1322 memset(&request, 0, sizeof(OrthancPluginHttpRequest)); |
1239 | 1344 |
1240 default: | 1345 default: |
1241 throw OrthancException(ErrorCode_InternalError); | 1346 throw OrthancException(ErrorCode_InternalError); |
1242 } | 1347 } |
1243 | 1348 |
1244 | 1349 request.groups = matcher.GetGroups(); |
1245 request.groups = (cgroups.size() ? &cgroups[0] : NULL); | 1350 request.groupsCount = matcher.GetGroupsCount(); |
1246 request.groupsCount = cgroups.size(); | |
1247 request.getCount = getArguments.size(); | 1351 request.getCount = getArguments.size(); |
1248 request.body = bodyData; | 1352 request.body = bodyData; |
1249 request.bodySize = bodySize; | 1353 request.bodySize = bodySize; |
1250 request.headersCount = headers.size(); | 1354 request.headersCount = headers.size(); |
1251 | 1355 |
1264 assert(callback != NULL); | 1368 assert(callback != NULL); |
1265 | 1369 |
1266 PImpl::PluginHttpOutput pluginOutput(output); | 1370 PImpl::PluginHttpOutput pluginOutput(output); |
1267 | 1371 |
1268 OrthancPluginErrorCode error = callback->Invoke | 1372 OrthancPluginErrorCode error = callback->Invoke |
1269 (pimpl_->restCallbackMutex_, pluginOutput, flatUri, request); | 1373 (pimpl_->restCallbackMutex_, pluginOutput, matcher.GetFlatUri(), request); |
1270 | 1374 |
1271 if (error == OrthancPluginErrorCode_Success && | 1375 pluginOutput.Close(error, GetErrorDictionary()); |
1272 output.IsWritingMultipart()) | 1376 return true; |
1273 { | |
1274 output.CloseMultipart(); | |
1275 } | |
1276 | |
1277 if (error == OrthancPluginErrorCode_Success) | |
1278 { | |
1279 return true; | |
1280 } | |
1281 else | |
1282 { | |
1283 GetErrorDictionary().LogError(error, false); | |
1284 | |
1285 if (pluginOutput.HasErrorDetails()) | |
1286 { | |
1287 throw OrthancException(static_cast<ErrorCode>(error), | |
1288 pluginOutput.GetErrorDetails(), | |
1289 pluginOutput.IsLogDetails()); | |
1290 } | |
1291 else | |
1292 { | |
1293 throw OrthancException(static_cast<ErrorCode>(error)); | |
1294 } | |
1295 } | |
1296 } | 1377 } |
1297 | 1378 |
1298 | 1379 |
1299 void OrthancPlugins::SignalStoredInstance(const std::string& instanceId, | 1380 void OrthancPlugins::SignalStoredInstance(const std::string& instanceId, |
1300 DicomInstanceToStore& instance, | 1381 DicomInstanceToStore& instance, |
1363 << p.pathRegularExpression; | 1444 << p.pathRegularExpression; |
1364 | 1445 |
1365 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock)); | 1446 pimpl_->restCallbacks_.push_back(new PImpl::RestCallback(p.pathRegularExpression, p.callback, lock)); |
1366 } | 1447 } |
1367 | 1448 |
1449 | |
1450 void OrthancPlugins::RegisterMultipartRestCallback(const void* parameters) | |
1451 { | |
1452 const _OrthancPluginMultipartRestCallback& p = | |
1453 *reinterpret_cast<const _OrthancPluginMultipartRestCallback*>(parameters); | |
1454 | |
1455 LOG(INFO) << "Plugin has registered a REST callback for multipart streams on: " | |
1456 << p.pathRegularExpression; | |
1457 | |
1458 pimpl_->multipartRestCallbacks_.push_back(new PImpl::MultipartRestCallback(p)); | |
1459 } | |
1368 | 1460 |
1369 | 1461 |
1370 void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters) | 1462 void OrthancPlugins::RegisterOnStoredInstanceCallback(const void* parameters) |
1371 { | 1463 { |
1372 const _OrthancPluginOnStoredInstanceCallback& p = | 1464 const _OrthancPluginOnStoredInstanceCallback& p = |
3449 | 3541 |
3450 case _OrthancPluginService_RegisterRestCallbackNoLock: | 3542 case _OrthancPluginService_RegisterRestCallbackNoLock: |
3451 RegisterRestCallback(parameters, false); | 3543 RegisterRestCallback(parameters, false); |
3452 return true; | 3544 return true; |
3453 | 3545 |
3546 case _OrthancPluginService_RegisterMultipartRestCallback: | |
3547 RegisterMultipartRestCallback(parameters); | |
3548 return true; | |
3549 | |
3454 case _OrthancPluginService_RegisterOnStoredInstanceCallback: | 3550 case _OrthancPluginService_RegisterOnStoredInstanceCallback: |
3455 RegisterOnStoredInstanceCallback(parameters); | 3551 RegisterOnStoredInstanceCallback(parameters); |
3456 return true; | 3552 return true; |
3457 | 3553 |
3458 case _OrthancPluginService_RegisterOnChangeCallback: | 3554 case _OrthancPluginService_RegisterOnChangeCallback: |
4009 } | 4105 } |
4010 } | 4106 } |
4011 } | 4107 } |
4012 | 4108 |
4013 | 4109 |
4110 class OrthancPlugins::MultipartStream : public IHttpHandler::IStream | |
4111 { | |
4112 private: | |
4113 OrthancPluginMultipartRestHandler* handler_; | |
4114 _OrthancPluginMultipartRestCallback parameters_; | |
4115 PluginsErrorDictionary& errorDictionary_; | |
4116 | |
4117 public: | |
4118 MultipartStream(OrthancPluginMultipartRestHandler* handler, | |
4119 const _OrthancPluginMultipartRestCallback& parameters, | |
4120 PluginsErrorDictionary& errorDictionary) : | |
4121 handler_(handler), | |
4122 parameters_(parameters), | |
4123 errorDictionary_(errorDictionary) | |
4124 { | |
4125 if (handler_ == NULL) | |
4126 { | |
4127 throw OrthancException(ErrorCode_Plugin, "The plugin has not created a multipart stream handler"); | |
4128 } | |
4129 } | |
4130 | |
4131 virtual ~MultipartStream() | |
4132 { | |
4133 if (handler_ != NULL) | |
4134 { | |
4135 parameters_.finalize(handler_); | |
4136 } | |
4137 } | |
4138 | |
4139 virtual void AddBodyChunk(const void* data, | |
4140 size_t size) | |
4141 { | |
4142 assert(handler_ != NULL); | |
4143 | |
4144 // TODO => multipart parsing | |
4145 } | |
4146 | |
4147 virtual void Execute(HttpOutput& output) | |
4148 { | |
4149 assert(handler_ != NULL); | |
4150 | |
4151 PImpl::PluginHttpOutput pluginOutput(output); | |
4152 | |
4153 OrthancPluginErrorCode error = parameters_.execute( | |
4154 handler_, reinterpret_cast<OrthancPluginRestOutput*>(&pluginOutput)); | |
4155 | |
4156 pluginOutput.Close(error, errorDictionary_); | |
4157 } | |
4158 }; | |
4159 | |
4160 | |
4014 IHttpHandler::IStream* OrthancPlugins::CreateStreamHandler(RequestOrigin origin, | 4161 IHttpHandler::IStream* OrthancPlugins::CreateStreamHandler(RequestOrigin origin, |
4015 const char* remoteIp, | 4162 const char* remoteIp, |
4016 const char* username, | 4163 const char* username, |
4017 HttpMethod method, | 4164 HttpMethod method, |
4018 const UriComponents& uri, | 4165 const UriComponents& uri, |
4019 const Arguments& headers) | 4166 const Arguments& headers) |
4020 { | 4167 { |
4021 // TODO - Plugins to install a handler for multipart body. | 4168 RestCallbackMatcher matcher(uri); |
4169 | |
4170 // Loop over the callbacks registered by the plugins | |
4171 for (PImpl::MultipartRestCallbacks::const_iterator it = pimpl_->multipartRestCallbacks_.begin(); | |
4172 it != pimpl_->multipartRestCallbacks_.end(); ++it) | |
4173 { | |
4174 if (matcher.IsMatch((*it)->GetRegularExpression())) | |
4175 { | |
4176 LOG(INFO) << "Delegating HTTP multipart request to plugin for URI: " << matcher.GetFlatUri(); | |
4177 | |
4178 std::vector<const char*> headersKeys, headersValues; | |
4179 ArgumentsToPlugin(headersKeys, headersValues, headers); | |
4180 | |
4181 OrthancPluginHttpMethod convertedMethod; | |
4182 switch (method) | |
4183 { | |
4184 case HttpMethod_Post: | |
4185 convertedMethod = OrthancPluginHttpMethod_Post; | |
4186 break; | |
4187 | |
4188 case HttpMethod_Put: | |
4189 convertedMethod = OrthancPluginHttpMethod_Put; | |
4190 break; | |
4191 | |
4192 default: | |
4193 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
4194 } | |
4195 | |
4196 std::string contentType = "TODO"; // TODO | |
4197 | |
4198 OrthancPluginMultipartRestHandler* handler = (*it)->GetParameters().factory( | |
4199 convertedMethod, matcher.GetFlatUri().c_str(), contentType.c_str(), | |
4200 matcher.GetGroupsCount(), matcher.GetGroups(), headers.size(), | |
4201 headers.empty() ? NULL : &headersKeys[0], | |
4202 headers.empty() ? NULL : &headersValues[0]); | |
4203 | |
4204 return new MultipartStream(handler, (*it)->GetParameters(), GetErrorDictionary()); | |
4205 } | |
4206 } | |
4022 | 4207 |
4023 return NULL; | 4208 return NULL; |
4024 } | 4209 } |
4025 } | 4210 } |