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 }