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 }