Mercurial > hg > orthanc
comparison OrthancFramework/Sources/RestApi/RestApiHierarchy.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | Core/RestApi/RestApiHierarchy.cpp@94f4a18a79cc |
children | bf7b9edf6b81 |
comparison
equal
deleted
inserted
replaced
4043:6c6239aec462 | 4044:d25f4c0fa160 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 #include "../PrecompiledHeaders.h" | |
35 #include "RestApiHierarchy.h" | |
36 | |
37 #include "../OrthancException.h" | |
38 | |
39 #include <cassert> | |
40 #include <stdio.h> | |
41 | |
42 namespace Orthanc | |
43 { | |
44 RestApiHierarchy::Resource::Resource() : | |
45 getHandler_(NULL), | |
46 postHandler_(NULL), | |
47 putHandler_(NULL), | |
48 deleteHandler_(NULL) | |
49 { | |
50 } | |
51 | |
52 | |
53 bool RestApiHierarchy::Resource::HasHandler(HttpMethod method) const | |
54 { | |
55 switch (method) | |
56 { | |
57 case HttpMethod_Get: | |
58 return getHandler_ != NULL; | |
59 | |
60 case HttpMethod_Post: | |
61 return postHandler_ != NULL; | |
62 | |
63 case HttpMethod_Put: | |
64 return putHandler_ != NULL; | |
65 | |
66 case HttpMethod_Delete: | |
67 return deleteHandler_ != NULL; | |
68 | |
69 default: | |
70 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
71 } | |
72 } | |
73 | |
74 | |
75 bool RestApiHierarchy::Resource::IsEmpty() const | |
76 { | |
77 return (getHandler_ == NULL && | |
78 postHandler_ == NULL && | |
79 putHandler_ == NULL && | |
80 deleteHandler_ == NULL); | |
81 } | |
82 | |
83 | |
84 RestApiHierarchy& RestApiHierarchy::AddChild(Children& children, | |
85 const std::string& name) | |
86 { | |
87 Children::iterator it = children.find(name); | |
88 | |
89 if (it == children.end()) | |
90 { | |
91 // Create new child | |
92 RestApiHierarchy *child = new RestApiHierarchy; | |
93 children[name] = child; | |
94 return *child; | |
95 } | |
96 else | |
97 { | |
98 return *it->second; | |
99 } | |
100 } | |
101 | |
102 | |
103 | |
104 bool RestApiHierarchy::Resource::Handle(RestApiGetCall& call) const | |
105 { | |
106 if (getHandler_ != NULL) | |
107 { | |
108 getHandler_(call); | |
109 return true; | |
110 } | |
111 else | |
112 { | |
113 return false; | |
114 } | |
115 } | |
116 | |
117 | |
118 bool RestApiHierarchy::Resource::Handle(RestApiPutCall& call) const | |
119 { | |
120 if (putHandler_ != NULL) | |
121 { | |
122 putHandler_(call); | |
123 return true; | |
124 } | |
125 else | |
126 { | |
127 return false; | |
128 } | |
129 } | |
130 | |
131 | |
132 bool RestApiHierarchy::Resource::Handle(RestApiPostCall& call) const | |
133 { | |
134 if (postHandler_ != NULL) | |
135 { | |
136 postHandler_(call); | |
137 return true; | |
138 } | |
139 else | |
140 { | |
141 return false; | |
142 } | |
143 } | |
144 | |
145 | |
146 bool RestApiHierarchy::Resource::Handle(RestApiDeleteCall& call) const | |
147 { | |
148 if (deleteHandler_ != NULL) | |
149 { | |
150 deleteHandler_(call); | |
151 return true; | |
152 } | |
153 else | |
154 { | |
155 return false; | |
156 } | |
157 } | |
158 | |
159 | |
160 | |
161 void RestApiHierarchy::DeleteChildren(Children& children) | |
162 { | |
163 for (Children::iterator it = children.begin(); | |
164 it != children.end(); ++it) | |
165 { | |
166 delete it->second; | |
167 } | |
168 } | |
169 | |
170 | |
171 template <typename Handler> | |
172 void RestApiHierarchy::RegisterInternal(const RestApiPath& path, | |
173 Handler handler, | |
174 size_t level) | |
175 { | |
176 if (path.GetLevelCount() == level) | |
177 { | |
178 if (path.IsUniversalTrailing()) | |
179 { | |
180 universalHandlers_.Register(handler); | |
181 } | |
182 else | |
183 { | |
184 handlers_.Register(handler); | |
185 } | |
186 } | |
187 else | |
188 { | |
189 RestApiHierarchy* child; | |
190 if (path.IsWildcardLevel(level)) | |
191 { | |
192 child = &AddChild(wildcardChildren_, path.GetWildcardName(level)); | |
193 } | |
194 else | |
195 { | |
196 child = &AddChild(children_, path.GetLevelName(level)); | |
197 } | |
198 | |
199 child->RegisterInternal(path, handler, level + 1); | |
200 } | |
201 } | |
202 | |
203 | |
204 bool RestApiHierarchy::LookupResource(IHttpHandler::Arguments& components, | |
205 const UriComponents& uri, | |
206 IVisitor& visitor, | |
207 size_t level) | |
208 { | |
209 if (uri.size() != 0 && | |
210 level > uri.size()) | |
211 { | |
212 return false; | |
213 } | |
214 | |
215 UriComponents trailing; | |
216 | |
217 // Look for an exact match on the resource of interest | |
218 if (uri.size() == 0 || | |
219 level == uri.size()) | |
220 { | |
221 if (!handlers_.IsEmpty() && | |
222 visitor.Visit(handlers_, uri, components, trailing)) | |
223 { | |
224 return true; | |
225 } | |
226 } | |
227 | |
228 | |
229 if (level < uri.size()) // A recursive call is possible | |
230 { | |
231 // Try and go down in the hierarchy, using an exact match for the child | |
232 Children::const_iterator child = children_.find(uri[level]); | |
233 if (child != children_.end()) | |
234 { | |
235 if (child->second->LookupResource(components, uri, visitor, level + 1)) | |
236 { | |
237 return true; | |
238 } | |
239 } | |
240 | |
241 // Try and go down in the hierarchy, using wildcard rules for children | |
242 for (child = wildcardChildren_.begin(); | |
243 child != wildcardChildren_.end(); ++child) | |
244 { | |
245 IHttpHandler::Arguments subComponents = components; | |
246 subComponents[child->first] = uri[level]; | |
247 | |
248 if (child->second->LookupResource(subComponents, uri, visitor, level + 1)) | |
249 { | |
250 return true; | |
251 } | |
252 } | |
253 } | |
254 | |
255 | |
256 // As a last resort, call the universal handlers, if any | |
257 if (!universalHandlers_.IsEmpty()) | |
258 { | |
259 trailing.resize(uri.size() - level); | |
260 size_t pos = 0; | |
261 for (size_t i = level; i < uri.size(); i++, pos++) | |
262 { | |
263 trailing[pos] = uri[i]; | |
264 } | |
265 | |
266 assert(pos == trailing.size()); | |
267 | |
268 if (visitor.Visit(universalHandlers_, uri, components, trailing)) | |
269 { | |
270 return true; | |
271 } | |
272 } | |
273 | |
274 return false; | |
275 } | |
276 | |
277 | |
278 bool RestApiHierarchy::CanGenerateDirectory() const | |
279 { | |
280 return (universalHandlers_.IsEmpty() && | |
281 wildcardChildren_.empty()); | |
282 } | |
283 | |
284 | |
285 bool RestApiHierarchy::GetDirectory(Json::Value& result, | |
286 const UriComponents& uri, | |
287 size_t level) | |
288 { | |
289 if (uri.size() == level) | |
290 { | |
291 if (CanGenerateDirectory()) | |
292 { | |
293 result = Json::arrayValue; | |
294 | |
295 for (Children::const_iterator it = children_.begin(); | |
296 it != children_.end(); ++it) | |
297 { | |
298 result.append(it->first); | |
299 } | |
300 | |
301 return true; | |
302 } | |
303 else | |
304 { | |
305 return false; | |
306 } | |
307 } | |
308 | |
309 Children::const_iterator child = children_.find(uri[level]); | |
310 if (child != children_.end()) | |
311 { | |
312 if (child->second->GetDirectory(result, uri, level + 1)) | |
313 { | |
314 return true; | |
315 } | |
316 } | |
317 | |
318 for (child = wildcardChildren_.begin(); | |
319 child != wildcardChildren_.end(); ++child) | |
320 { | |
321 if (child->second->GetDirectory(result, uri, level + 1)) | |
322 { | |
323 return true; | |
324 } | |
325 } | |
326 | |
327 return false; | |
328 } | |
329 | |
330 | |
331 RestApiHierarchy::~RestApiHierarchy() | |
332 { | |
333 DeleteChildren(children_); | |
334 DeleteChildren(wildcardChildren_); | |
335 } | |
336 | |
337 void RestApiHierarchy::Register(const std::string& uri, | |
338 RestApiGetCall::Handler handler) | |
339 { | |
340 RestApiPath path(uri); | |
341 RegisterInternal(path, handler, 0); | |
342 } | |
343 | |
344 void RestApiHierarchy::Register(const std::string& uri, | |
345 RestApiPutCall::Handler handler) | |
346 { | |
347 RestApiPath path(uri); | |
348 RegisterInternal(path, handler, 0); | |
349 } | |
350 | |
351 void RestApiHierarchy::Register(const std::string& uri, | |
352 RestApiPostCall::Handler handler) | |
353 { | |
354 RestApiPath path(uri); | |
355 RegisterInternal(path, handler, 0); | |
356 } | |
357 | |
358 void RestApiHierarchy::Register(const std::string& uri, | |
359 RestApiDeleteCall::Handler handler) | |
360 { | |
361 RestApiPath path(uri); | |
362 RegisterInternal(path, handler, 0); | |
363 } | |
364 | |
365 void RestApiHierarchy::CreateSiteMap(Json::Value& target) const | |
366 { | |
367 target = Json::objectValue; | |
368 | |
369 /*std::string s = " "; | |
370 if (handlers_.HasHandler(HttpMethod_Get)) | |
371 { | |
372 s += "GET "; | |
373 } | |
374 | |
375 if (handlers_.HasHandler(HttpMethod_Post)) | |
376 { | |
377 s += "POST "; | |
378 } | |
379 | |
380 if (handlers_.HasHandler(HttpMethod_Put)) | |
381 { | |
382 s += "PUT "; | |
383 } | |
384 | |
385 if (handlers_.HasHandler(HttpMethod_Delete)) | |
386 { | |
387 s += "DELETE "; | |
388 } | |
389 | |
390 target = s;*/ | |
391 | |
392 for (Children::const_iterator it = children_.begin(); | |
393 it != children_.end(); ++it) | |
394 { | |
395 it->second->CreateSiteMap(target[it->first]); | |
396 } | |
397 | |
398 for (Children::const_iterator it = wildcardChildren_.begin(); | |
399 it != wildcardChildren_.end(); ++it) | |
400 { | |
401 it->second->CreateSiteMap(target["<" + it->first + ">"]); | |
402 } | |
403 } | |
404 | |
405 | |
406 bool RestApiHierarchy::LookupResource(const UriComponents& uri, | |
407 IVisitor& visitor) | |
408 { | |
409 IHttpHandler::Arguments components; | |
410 return LookupResource(components, uri, visitor, 0); | |
411 } | |
412 | |
413 | |
414 | |
415 namespace | |
416 { | |
417 // Anonymous namespace to avoid clashes between compilation modules | |
418 | |
419 class AcceptedMethodsVisitor : public RestApiHierarchy::IVisitor | |
420 { | |
421 private: | |
422 std::set<HttpMethod>& methods_; | |
423 | |
424 public: | |
425 AcceptedMethodsVisitor(std::set<HttpMethod>& methods) : methods_(methods) | |
426 { | |
427 } | |
428 | |
429 virtual bool Visit(const RestApiHierarchy::Resource& resource, | |
430 const UriComponents& uri, | |
431 const IHttpHandler::Arguments& components, | |
432 const UriComponents& trailing) | |
433 { | |
434 if (trailing.size() == 0) // Ignore universal handlers | |
435 { | |
436 if (resource.HasHandler(HttpMethod_Get)) | |
437 { | |
438 methods_.insert(HttpMethod_Get); | |
439 } | |
440 | |
441 if (resource.HasHandler(HttpMethod_Post)) | |
442 { | |
443 methods_.insert(HttpMethod_Post); | |
444 } | |
445 | |
446 if (resource.HasHandler(HttpMethod_Put)) | |
447 { | |
448 methods_.insert(HttpMethod_Put); | |
449 } | |
450 | |
451 if (resource.HasHandler(HttpMethod_Delete)) | |
452 { | |
453 methods_.insert(HttpMethod_Delete); | |
454 } | |
455 } | |
456 | |
457 return false; // Continue to check all the possible ways to access this URI | |
458 } | |
459 }; | |
460 } | |
461 | |
462 void RestApiHierarchy::GetAcceptedMethods(std::set<HttpMethod>& methods, | |
463 const UriComponents& uri) | |
464 { | |
465 IHttpHandler::Arguments components; | |
466 AcceptedMethodsVisitor visitor(methods); | |
467 if (LookupResource(components, uri, visitor, 0)) | |
468 { | |
469 Json::Value d; | |
470 if (GetDirectory(d, uri)) | |
471 { | |
472 methods.insert(HttpMethod_Get); | |
473 } | |
474 } | |
475 } | |
476 } |