Mercurial > hg > orthanc
comparison Plugins/Engine/PluginsHttpHandler.cpp @ 1018:564e39d6df13 lua-scripting
integration mainline->lua-scripting
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 10 Jul 2014 11:31:14 +0200 |
parents | 501880d76474 |
children | 6208ab500ffd |
comparison
equal
deleted
inserted
replaced
1017:9d0c7301596e | 1018:564e39d6df13 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, | |
4 * Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #include "PluginsHttpHandler.h" | |
34 | |
35 #include "../../Core/ChunkedBuffer.h" | |
36 #include "../../Core/OrthancException.h" | |
37 #include "../../Core/Toolbox.h" | |
38 #include "../../Core/HttpServer/HttpOutput.h" | |
39 #include "../../Core/ImageFormats/PngWriter.h" | |
40 | |
41 #include <boost/regex.hpp> | |
42 #include <glog/logging.h> | |
43 | |
44 namespace Orthanc | |
45 { | |
46 namespace | |
47 { | |
48 // Anonymous namespace to avoid clashes between compilation modules | |
49 class StringHttpOutput : public IHttpOutputStream | |
50 { | |
51 private: | |
52 ChunkedBuffer buffer_; | |
53 | |
54 public: | |
55 void GetOutput(std::string& output) | |
56 { | |
57 buffer_.Flatten(output); | |
58 } | |
59 | |
60 virtual void OnHttpStatusReceived(HttpStatus status) | |
61 { | |
62 if (status != HttpStatus_200_Ok) | |
63 { | |
64 throw OrthancException(ErrorCode_BadRequest); | |
65 } | |
66 } | |
67 | |
68 virtual void Send(bool isHeader, const void* buffer, size_t length) | |
69 { | |
70 if (!isHeader) | |
71 { | |
72 buffer_.AddChunk(reinterpret_cast<const char*>(buffer), length); | |
73 } | |
74 } | |
75 }; | |
76 } | |
77 | |
78 | |
79 | |
80 struct PluginsHttpHandler::PImpl | |
81 { | |
82 typedef std::pair<boost::regex*, OrthancPluginRestCallback> Callback; | |
83 typedef std::list<Callback> Callbacks; | |
84 | |
85 ServerContext& context_; | |
86 Callbacks callbacks_; | |
87 OrthancRestApi* restApi_; | |
88 | |
89 PImpl(ServerContext& context) : context_(context), restApi_(NULL) | |
90 { | |
91 } | |
92 }; | |
93 | |
94 | |
95 PluginsHttpHandler::PluginsHttpHandler(ServerContext& context) | |
96 { | |
97 pimpl_.reset(new PImpl(context)); | |
98 } | |
99 | |
100 | |
101 PluginsHttpHandler::~PluginsHttpHandler() | |
102 { | |
103 for (PImpl::Callbacks::iterator it = pimpl_->callbacks_.begin(); | |
104 it != pimpl_->callbacks_.end(); ++it) | |
105 { | |
106 delete it->first; | |
107 } | |
108 } | |
109 | |
110 | |
111 bool PluginsHttpHandler::Handle(HttpOutput& output, | |
112 HttpMethod method, | |
113 const UriComponents& uri, | |
114 const Arguments& headers, | |
115 const Arguments& getArguments, | |
116 const std::string& postData) | |
117 { | |
118 std::string flatUri = Toolbox::FlattenUri(uri); | |
119 OrthancPluginRestCallback callback = NULL; | |
120 | |
121 std::vector<std::string> groups; | |
122 std::vector<const char*> cgroups; | |
123 | |
124 bool found = false; | |
125 for (PImpl::Callbacks::const_iterator it = pimpl_->callbacks_.begin(); | |
126 it != pimpl_->callbacks_.end() && !found; ++it) | |
127 { | |
128 boost::cmatch what; | |
129 if (boost::regex_match(flatUri.c_str(), what, *(it->first))) | |
130 { | |
131 callback = it->second; | |
132 | |
133 if (what.size() > 1) | |
134 { | |
135 groups.resize(what.size() - 1); | |
136 cgroups.resize(what.size() - 1); | |
137 for (size_t i = 1; i < what.size(); i++) | |
138 { | |
139 groups[i - 1] = what[i]; | |
140 cgroups[i - 1] = groups[i - 1].c_str(); | |
141 } | |
142 } | |
143 | |
144 found = true; | |
145 } | |
146 } | |
147 | |
148 if (!found) | |
149 { | |
150 return false; | |
151 } | |
152 | |
153 LOG(INFO) << "Delegating HTTP request to plugin for URI: " << flatUri; | |
154 | |
155 std::vector<const char*> getKeys(getArguments.size()); | |
156 std::vector<const char*> getValues(getArguments.size()); | |
157 | |
158 OrthancPluginHttpRequest request; | |
159 memset(&request, 0, sizeof(OrthancPluginHttpRequest)); | |
160 | |
161 switch (method) | |
162 { | |
163 case HttpMethod_Get: | |
164 { | |
165 request.method = OrthancPluginHttpMethod_Get; | |
166 | |
167 size_t i = 0; | |
168 for (Arguments::const_iterator it = getArguments.begin(); | |
169 it != getArguments.end(); ++it, ++i) | |
170 { | |
171 getKeys[i] = it->first.c_str(); | |
172 getValues[i] = it->second.c_str(); | |
173 } | |
174 | |
175 break; | |
176 } | |
177 | |
178 case HttpMethod_Post: | |
179 request.method = OrthancPluginHttpMethod_Post; | |
180 break; | |
181 | |
182 case HttpMethod_Delete: | |
183 request.method = OrthancPluginHttpMethod_Delete; | |
184 break; | |
185 | |
186 case HttpMethod_Put: | |
187 request.method = OrthancPluginHttpMethod_Put; | |
188 break; | |
189 | |
190 default: | |
191 throw OrthancException(ErrorCode_InternalError); | |
192 } | |
193 | |
194 | |
195 request.groups = (cgroups.size() ? &cgroups[0] : NULL); | |
196 request.groupsCount = cgroups.size(); | |
197 request.getCount = getArguments.size(); | |
198 request.body = (postData.size() ? &postData[0] : NULL); | |
199 request.bodySize = postData.size(); | |
200 | |
201 if (getArguments.size() > 0) | |
202 { | |
203 request.getKeys = &getKeys[0]; | |
204 request.getValues = &getValues[0]; | |
205 } | |
206 | |
207 assert(callback != NULL); | |
208 int32_t error = callback(reinterpret_cast<OrthancPluginRestOutput*>(&output), | |
209 flatUri.c_str(), | |
210 &request); | |
211 | |
212 if (error < 0) | |
213 { | |
214 LOG(ERROR) << "Plugin failed with error code " << error; | |
215 return false; | |
216 } | |
217 else | |
218 { | |
219 if (error > 0) | |
220 { | |
221 LOG(WARNING) << "Plugin finished with warning code " << error; | |
222 } | |
223 | |
224 return true; | |
225 } | |
226 } | |
227 | |
228 | |
229 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, | |
230 const void* data, | |
231 size_t size) | |
232 { | |
233 target.size = size; | |
234 | |
235 if (size == 0) | |
236 { | |
237 target.data = NULL; | |
238 } | |
239 else | |
240 { | |
241 target.data = malloc(size); | |
242 if (target.data != NULL) | |
243 { | |
244 memcpy(target.data, data, size); | |
245 } | |
246 else | |
247 { | |
248 throw OrthancException(ErrorCode_NotEnoughMemory); | |
249 } | |
250 } | |
251 } | |
252 | |
253 | |
254 static void CopyToMemoryBuffer(OrthancPluginMemoryBuffer& target, | |
255 const std::string& str) | |
256 { | |
257 if (str.size() == 0) | |
258 { | |
259 target.size = 0; | |
260 target.data = NULL; | |
261 } | |
262 else | |
263 { | |
264 CopyToMemoryBuffer(target, str.c_str(), str.size()); | |
265 } | |
266 } | |
267 | |
268 | |
269 void PluginsHttpHandler::RegisterRestCallback(const void* parameters) | |
270 { | |
271 const _OrthancPluginRestCallback& p = | |
272 *reinterpret_cast<const _OrthancPluginRestCallback*>(parameters); | |
273 | |
274 LOG(INFO) << "Plugin has registered a REST callback on: " << p.pathRegularExpression; | |
275 pimpl_->callbacks_.push_back(std::make_pair(new boost::regex(p.pathRegularExpression), p.callback)); | |
276 } | |
277 | |
278 | |
279 | |
280 void PluginsHttpHandler::AnswerBuffer(const void* parameters) | |
281 { | |
282 const _OrthancPluginAnswerBuffer& p = | |
283 *reinterpret_cast<const _OrthancPluginAnswerBuffer*>(parameters); | |
284 | |
285 HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output); | |
286 translatedOutput->AnswerBufferWithContentType(p.answer, p.answerSize, p.mimeType); | |
287 } | |
288 | |
289 | |
290 void PluginsHttpHandler::Redirect(const void* parameters) | |
291 { | |
292 const _OrthancPluginRedirect& p = | |
293 *reinterpret_cast<const _OrthancPluginRedirect*>(parameters); | |
294 | |
295 HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output); | |
296 translatedOutput->Redirect(p.redirection); | |
297 } | |
298 | |
299 | |
300 void PluginsHttpHandler::CompressAndAnswerPngImage(const void* parameters) | |
301 { | |
302 const _OrthancPluginCompressAndAnswerPngImage& p = | |
303 *reinterpret_cast<const _OrthancPluginCompressAndAnswerPngImage*>(parameters); | |
304 | |
305 HttpOutput* translatedOutput = reinterpret_cast<HttpOutput*>(p.output); | |
306 | |
307 PixelFormat format; | |
308 switch (p.format) | |
309 { | |
310 case OrthancPluginPixelFormat_Grayscale8: | |
311 format = PixelFormat_Grayscale8; | |
312 break; | |
313 | |
314 case OrthancPluginPixelFormat_Grayscale16: | |
315 format = PixelFormat_Grayscale16; | |
316 break; | |
317 | |
318 case OrthancPluginPixelFormat_SignedGrayscale16: | |
319 format = PixelFormat_SignedGrayscale16; | |
320 break; | |
321 | |
322 case OrthancPluginPixelFormat_RGB24: | |
323 format = PixelFormat_RGB24; | |
324 break; | |
325 | |
326 case OrthancPluginPixelFormat_RGBA32: | |
327 format = PixelFormat_RGBA32; | |
328 break; | |
329 | |
330 default: | |
331 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
332 } | |
333 | |
334 ImageAccessor accessor; | |
335 accessor.AssignReadOnly(format, p.width, p.height, p.pitch, p.buffer); | |
336 | |
337 PngWriter writer; | |
338 std::string png; | |
339 writer.WriteToMemory(png, accessor); | |
340 | |
341 translatedOutput->AnswerBufferWithContentType(png, "image/png"); | |
342 } | |
343 | |
344 | |
345 void PluginsHttpHandler::GetDicomForInstance(const void* parameters) | |
346 { | |
347 const _OrthancPluginGetDicomForInstance& p = | |
348 *reinterpret_cast<const _OrthancPluginGetDicomForInstance*>(parameters); | |
349 | |
350 std::string dicom; | |
351 pimpl_->context_.ReadFile(dicom, p.instanceId, FileContentType_Dicom); | |
352 CopyToMemoryBuffer(*p.target, dicom); | |
353 } | |
354 | |
355 | |
356 void PluginsHttpHandler::RestApiGet(const void* parameters) | |
357 { | |
358 const _OrthancPluginRestApiGet& p = | |
359 *reinterpret_cast<const _OrthancPluginRestApiGet*>(parameters); | |
360 | |
361 HttpHandler::Arguments headers; // No HTTP header | |
362 std::string body; // No body for a GET request | |
363 | |
364 UriComponents uri; | |
365 HttpHandler::Arguments getArguments; | |
366 HttpHandler::ParseGetQuery(uri, getArguments, p.uri); | |
367 | |
368 StringHttpOutput stream; | |
369 HttpOutput http(stream); | |
370 | |
371 LOG(INFO) << "Plugin making REST GET call on URI " << p.uri; | |
372 | |
373 if (pimpl_->restApi_ != NULL && | |
374 pimpl_->restApi_->Handle(http, HttpMethod_Get, uri, headers, getArguments, body)) | |
375 { | |
376 std::string result; | |
377 stream.GetOutput(result); | |
378 CopyToMemoryBuffer(*p.target, result); | |
379 } | |
380 else | |
381 { | |
382 throw OrthancException(ErrorCode_BadRequest); | |
383 } | |
384 } | |
385 | |
386 | |
387 void PluginsHttpHandler::RestApiPostPut(bool isPost, const void* parameters) | |
388 { | |
389 const _OrthancPluginRestApiPostPut& p = | |
390 *reinterpret_cast<const _OrthancPluginRestApiPostPut*>(parameters); | |
391 | |
392 HttpHandler::Arguments headers; // No HTTP header | |
393 HttpHandler::Arguments getArguments; // No GET argument for POST/PUT | |
394 | |
395 UriComponents uri; | |
396 Toolbox::SplitUriComponents(uri, p.uri); | |
397 | |
398 // TODO Avoid unecessary memcpy | |
399 std::string body(p.body, p.bodySize); | |
400 | |
401 StringHttpOutput stream; | |
402 HttpOutput http(stream); | |
403 | |
404 HttpMethod method = (isPost ? HttpMethod_Post : HttpMethod_Put); | |
405 LOG(INFO) << "Plugin making REST " << EnumerationToString(method) << " call on URI " << p.uri; | |
406 | |
407 if (pimpl_->restApi_ != NULL && | |
408 pimpl_->restApi_->Handle(http, method, uri, headers, getArguments, body)) | |
409 { | |
410 std::string result; | |
411 stream.GetOutput(result); | |
412 CopyToMemoryBuffer(*p.target, result); | |
413 } | |
414 else | |
415 { | |
416 throw OrthancException(ErrorCode_BadRequest); | |
417 } | |
418 } | |
419 | |
420 | |
421 void PluginsHttpHandler::RestApiDelete(const void* parameters) | |
422 { | |
423 // The "parameters" point to the URI | |
424 UriComponents uri; | |
425 Toolbox::SplitUriComponents(uri, reinterpret_cast<const char*>(parameters)); | |
426 | |
427 HttpHandler::Arguments headers; // No HTTP header | |
428 HttpHandler::Arguments getArguments; // No GET argument for POST/PUT | |
429 std::string body; // No body for DELETE | |
430 | |
431 StringHttpOutput stream; | |
432 HttpOutput http(stream); | |
433 | |
434 LOG(INFO) << "Plugin making REST DELETE call on URI " | |
435 << reinterpret_cast<const char*>(parameters); | |
436 | |
437 if (pimpl_->restApi_ == NULL || | |
438 !pimpl_->restApi_->Handle(http, HttpMethod_Delete, uri, headers, getArguments, body)) | |
439 { | |
440 throw OrthancException(ErrorCode_BadRequest); | |
441 } | |
442 } | |
443 | |
444 | |
445 bool PluginsHttpHandler::InvokeService(_OrthancPluginService service, | |
446 const void* parameters) | |
447 { | |
448 switch (service) | |
449 { | |
450 case _OrthancPluginService_RegisterRestCallback: | |
451 RegisterRestCallback(parameters); | |
452 return true; | |
453 | |
454 case _OrthancPluginService_AnswerBuffer: | |
455 AnswerBuffer(parameters); | |
456 return true; | |
457 | |
458 case _OrthancPluginService_CompressAndAnswerPngImage: | |
459 CompressAndAnswerPngImage(parameters); | |
460 return true; | |
461 | |
462 case _OrthancPluginService_GetDicomForInstance: | |
463 GetDicomForInstance(parameters); | |
464 return true; | |
465 | |
466 case _OrthancPluginService_RestApiGet: | |
467 RestApiGet(parameters); | |
468 return true; | |
469 | |
470 case _OrthancPluginService_RestApiPost: | |
471 RestApiPostPut(true, parameters); | |
472 return true; | |
473 | |
474 case _OrthancPluginService_RestApiDelete: | |
475 RestApiDelete(parameters); | |
476 return true; | |
477 | |
478 case _OrthancPluginService_RestApiPut: | |
479 RestApiPostPut(false, parameters); | |
480 return true; | |
481 | |
482 case _OrthancPluginService_Redirect: | |
483 Redirect(parameters); | |
484 return true; | |
485 | |
486 default: | |
487 return false; | |
488 } | |
489 } | |
490 | |
491 | |
492 void PluginsHttpHandler::SetOrthancRestApi(OrthancRestApi& restApi) | |
493 { | |
494 pimpl_->restApi_ = &restApi; | |
495 } | |
496 | |
497 } |