Mercurial > hg > orthanc-stone
comparison Applications/Platforms/WebAssembly/WebAssemblyOracle.cpp @ 1591:5887a4f8594b
moving platform-specific files out of the "OrthancStone" folder
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 23 Oct 2020 13:15:03 +0200 |
parents | OrthancStone/Sources/Oracle/WebAssemblyOracle.cpp@1b3039384972 |
children | 4fb8fdf03314 |
comparison
equal
deleted
inserted
replaced
1590:7b963bccafef | 1591:5887a4f8594b |
---|---|
1 /** | |
2 * Stone of Orthanc | |
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 Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #if defined(ORTHANC_BUILDING_STONE_LIBRARY) && ORTHANC_BUILDING_STONE_LIBRARY == 1 | |
23 # include "WebAssemblyOracle_Includes.h" | |
24 #else | |
25 // This is the case when using the WebAssembly side module, and this | |
26 // source file must be compiled within the WebAssembly main module | |
27 # include <Oracle/WebAssemblyOracle_Includes.h> | |
28 #endif | |
29 | |
30 #include <OrthancException.h> | |
31 #include <Toolbox.h> | |
32 | |
33 #include <emscripten.h> | |
34 #include <emscripten/html5.h> | |
35 #include <emscripten/fetch.h> | |
36 | |
37 | |
38 #if ORTHANC_ENABLE_DCMTK == 1 | |
39 static unsigned int BUCKET_SOP = 1; | |
40 #endif | |
41 | |
42 | |
43 namespace OrthancStone | |
44 { | |
45 class WebAssemblyOracle::TimeoutContext | |
46 { | |
47 private: | |
48 WebAssemblyOracle& oracle_; | |
49 boost::weak_ptr<IObserver> receiver_; | |
50 std::unique_ptr<SleepOracleCommand> command_; | |
51 | |
52 public: | |
53 TimeoutContext(WebAssemblyOracle& oracle, | |
54 boost::weak_ptr<IObserver> receiver, | |
55 IOracleCommand* command) : | |
56 oracle_(oracle), | |
57 receiver_(receiver) | |
58 { | |
59 if (command == NULL) | |
60 { | |
61 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
62 } | |
63 else | |
64 { | |
65 command_.reset(dynamic_cast<SleepOracleCommand*>(command)); | |
66 } | |
67 } | |
68 | |
69 void EmitMessage() | |
70 { | |
71 assert(command_.get() != NULL); | |
72 | |
73 SleepOracleCommand::TimeoutMessage message(*command_); | |
74 oracle_.EmitMessage(receiver_, message); | |
75 } | |
76 | |
77 static void Callback(void *userData) | |
78 { | |
79 std::unique_ptr<TimeoutContext> context(reinterpret_cast<TimeoutContext*>(userData)); | |
80 context->EmitMessage(); | |
81 } | |
82 }; | |
83 | |
84 | |
85 /** | |
86 This object is created on the heap for every http request. | |
87 It is deleted in the success (or error) callbacks. | |
88 | |
89 This object references the receiver of the request. Since this is a raw | |
90 reference, we need additional checks to make sure we send the response to | |
91 the same object, for the object can be deleted and a new one recreated at the | |
92 same address (it often happens in the [single-threaded] browser context). | |
93 */ | |
94 class WebAssemblyOracle::FetchContext : public boost::noncopyable | |
95 { | |
96 private: | |
97 WebAssemblyOracle& oracle_; | |
98 boost::weak_ptr<IObserver> receiver_; | |
99 std::unique_ptr<IOracleCommand> command_; | |
100 std::string expectedContentType_; | |
101 | |
102 public: | |
103 FetchContext(WebAssemblyOracle& oracle, | |
104 boost::weak_ptr<IObserver> receiver, | |
105 IOracleCommand* command, | |
106 const std::string& expectedContentType) : | |
107 oracle_(oracle), | |
108 receiver_(receiver), | |
109 command_(command), | |
110 expectedContentType_(expectedContentType) | |
111 { | |
112 if (Orthanc::Logging::IsTraceLevelEnabled()) | |
113 { | |
114 // Calling "receiver.lock()" is expensive, hence the quick check if TRACE is enabled | |
115 LOG(TRACE) << "WebAssemblyOracle::FetchContext::FetchContext() | " | |
116 << "receiver address = " << std::hex << receiver.lock().get(); | |
117 } | |
118 | |
119 if (command == NULL) | |
120 { | |
121 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
122 } | |
123 } | |
124 | |
125 const std::string& GetExpectedContentType() const | |
126 { | |
127 return expectedContentType_; | |
128 } | |
129 | |
130 IMessageEmitter& GetEmitter() const | |
131 { | |
132 return oracle_; | |
133 } | |
134 | |
135 boost::weak_ptr<IObserver> GetReceiver() const | |
136 { | |
137 return receiver_; | |
138 } | |
139 | |
140 void EmitMessage(const IMessage& message) | |
141 { | |
142 if (Orthanc::Logging::IsTraceLevelEnabled()) | |
143 { | |
144 // Calling "receiver_.lock()" is expensive, hence the quick check if TRACE is enabled | |
145 LOG(TRACE) << "WebAssemblyOracle::FetchContext::EmitMessage receiver_ = " | |
146 << std::hex << receiver_.lock().get() << std::dec; | |
147 } | |
148 | |
149 oracle_.EmitMessage(receiver_, message); | |
150 } | |
151 | |
152 IOracleCommand& GetCommand() const | |
153 { | |
154 return *command_; | |
155 } | |
156 | |
157 template <typename T> | |
158 const T& GetTypedCommand() const | |
159 { | |
160 return dynamic_cast<T&>(*command_); | |
161 } | |
162 | |
163 #if ORTHANC_ENABLE_DCMTK == 1 | |
164 void StoreInCache(const std::string& sopInstanceUid, | |
165 std::unique_ptr<Orthanc::ParsedDicomFile>& dicom, | |
166 size_t fileSize) | |
167 { | |
168 if (oracle_.dicomCache_.get()) | |
169 { | |
170 // Store it into the cache for future use | |
171 oracle_.dicomCache_->Acquire(BUCKET_SOP, sopInstanceUid, | |
172 dicom.release(), fileSize, true); | |
173 } | |
174 } | |
175 #endif | |
176 | |
177 static void SuccessCallback(emscripten_fetch_t *fetch) | |
178 { | |
179 /** | |
180 * Firstly, make a local copy of the fetched information, and | |
181 * free data associated with the fetch. | |
182 **/ | |
183 | |
184 if (fetch->userData == NULL) | |
185 { | |
186 LOG(ERROR) << "WebAssemblyOracle::FetchContext::SuccessCallback fetch->userData is NULL!!!!!!!"; | |
187 return; | |
188 } | |
189 | |
190 std::unique_ptr<FetchContext> context(reinterpret_cast<FetchContext*>(fetch->userData)); | |
191 | |
192 std::string answer; | |
193 if (fetch->numBytes > 0) | |
194 { | |
195 answer.assign(fetch->data, fetch->numBytes); | |
196 } | |
197 | |
198 | |
199 /** | |
200 * Retrieving the headers of the HTTP answer. | |
201 **/ | |
202 HttpHeaders headers; | |
203 | |
204 #if (__EMSCRIPTEN_major__ < 1 || \ | |
205 (__EMSCRIPTEN_major__ == 1 && __EMSCRIPTEN_minor__ < 38) || \ | |
206 (__EMSCRIPTEN_major__ == 1 && __EMSCRIPTEN_minor__ == 38 && __EMSCRIPTEN_tiny__ < 37)) | |
207 # warning Consider upgrading Emscripten to a version above 1.38.37, incomplete support of Fetch API | |
208 | |
209 /** | |
210 * HACK - If emscripten < 1.38.37, the fetch API does not | |
211 * contain a way to retrieve the HTTP headers of the answer. We | |
212 * make the assumption that the "Content-Type" header of the | |
213 * response is the same as the "Accept" header of the | |
214 * query. This is fixed thanks to the | |
215 * "emscripten_fetch_get_response_headers()" function that was | |
216 * added to "fetch.h" at emscripten-1.38.37 on 2019-06-26. | |
217 * | |
218 * https://github.com/emscripten-core/emscripten/blob/1.38.37/system/include/emscripten/fetch.h | |
219 * https://github.com/emscripten-core/emscripten/pull/8486 | |
220 **/ | |
221 if (fetch->userData != NULL) | |
222 { | |
223 if (!context->GetExpectedContentType().empty()) | |
224 { | |
225 headers["Content-Type"] = context->GetExpectedContentType(); | |
226 } | |
227 } | |
228 #else | |
229 { | |
230 size_t size = emscripten_fetch_get_response_headers_length(fetch); | |
231 | |
232 std::string plainHeaders(size + 1, '\0'); | |
233 emscripten_fetch_get_response_headers(fetch, &plainHeaders[0], size + 1); | |
234 | |
235 std::vector<std::string> tokens; | |
236 Orthanc::Toolbox::TokenizeString(tokens, plainHeaders, '\n'); | |
237 | |
238 for (size_t i = 0; i < tokens.size(); i++) | |
239 { | |
240 size_t p = tokens[i].find(':'); | |
241 if (p != std::string::npos) | |
242 { | |
243 std::string key = Orthanc::Toolbox::StripSpaces(tokens[i].substr(0, p)); | |
244 std::string value = Orthanc::Toolbox::StripSpaces(tokens[i].substr(p + 1)); | |
245 headers[key] = value; | |
246 } | |
247 } | |
248 } | |
249 #endif | |
250 | |
251 LOG(TRACE) << "About to call emscripten_fetch_close"; | |
252 emscripten_fetch_close(fetch); | |
253 LOG(TRACE) << "Successfully called emscripten_fetch_close"; | |
254 | |
255 /** | |
256 * Secondly, use the retrieved data. | |
257 * IMPORTANT NOTE: the receiver might be dead. This is prevented | |
258 * by the object responsible for zombie check, later on. | |
259 **/ | |
260 try | |
261 { | |
262 if (context.get() == NULL) | |
263 { | |
264 LOG(ERROR) << "WebAssemblyOracle::FetchContext::SuccessCallback: (context.get() == NULL)"; | |
265 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
266 } | |
267 else | |
268 { | |
269 switch (context->GetCommand().GetType()) | |
270 { | |
271 case IOracleCommand::Type_Http: | |
272 { | |
273 HttpCommand::SuccessMessage message(context->GetTypedCommand<HttpCommand>(), headers, answer); | |
274 context->EmitMessage(message); | |
275 break; | |
276 } | |
277 | |
278 case IOracleCommand::Type_OrthancRestApi: | |
279 { | |
280 LOG(TRACE) << "WebAssemblyOracle::FetchContext::SuccessCallback. About to call context->EmitMessage(message);"; | |
281 OrthancRestApiCommand::SuccessMessage message | |
282 (context->GetTypedCommand<OrthancRestApiCommand>(), headers, answer); | |
283 context->EmitMessage(message); | |
284 break; | |
285 } | |
286 | |
287 case IOracleCommand::Type_GetOrthancImage: | |
288 { | |
289 context->GetTypedCommand<GetOrthancImageCommand>().ProcessHttpAnswer | |
290 (context->GetReceiver(), context->GetEmitter(), answer, headers); | |
291 break; | |
292 } | |
293 | |
294 case IOracleCommand::Type_GetOrthancWebViewerJpeg: | |
295 { | |
296 context->GetTypedCommand<GetOrthancWebViewerJpegCommand>().ProcessHttpAnswer | |
297 (context->GetReceiver(), context->GetEmitter(), answer); | |
298 break; | |
299 } | |
300 | |
301 case IOracleCommand::Type_ParseDicomFromWado: | |
302 { | |
303 #if ORTHANC_ENABLE_DCMTK == 1 | |
304 const ParseDicomFromWadoCommand& command = | |
305 context->GetTypedCommand<ParseDicomFromWadoCommand>(); | |
306 | |
307 size_t fileSize; | |
308 std::unique_ptr<Orthanc::ParsedDicomFile> dicom | |
309 (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers)); | |
310 | |
311 { | |
312 ParseDicomSuccessMessage message(command, command.GetSource(), *dicom, fileSize, true); | |
313 context->EmitMessage(message); | |
314 } | |
315 | |
316 context->StoreInCache(command.GetSopInstanceUid(), dicom, fileSize); | |
317 #else | |
318 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
319 #endif | |
320 break; | |
321 } | |
322 | |
323 default: | |
324 LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in SuccessCallback): " | |
325 << context->GetCommand().GetType(); | |
326 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
327 } | |
328 } | |
329 } | |
330 catch (Orthanc::OrthancException& e) | |
331 { | |
332 LOG(INFO) << "Error while processing a fetch answer in the oracle: " << e.What(); | |
333 | |
334 { | |
335 OracleCommandExceptionMessage message(context->GetCommand(), e); | |
336 context->EmitMessage(message); | |
337 } | |
338 } | |
339 } | |
340 | |
341 static void FailureCallback(emscripten_fetch_t *fetch) | |
342 { | |
343 std::unique_ptr<FetchContext> context(reinterpret_cast<FetchContext*>(fetch->userData)); | |
344 | |
345 #if 0 | |
346 { | |
347 const size_t kEmscriptenStatusTextSize = sizeof(emscripten_fetch_t::statusText); | |
348 char message[kEmscriptenStatusTextSize + 1]; | |
349 memcpy(message, fetch->statusText, kEmscriptenStatusTextSize); | |
350 message[kEmscriptenStatusTextSize] = 0; | |
351 | |
352 LOG(ERROR) << "Fetching " << fetch->url | |
353 << " failed, HTTP failure status code: " << fetch->status | |
354 << " | statusText = " << message | |
355 << " | numBytes = " << fetch->numBytes | |
356 << " | totalBytes = " << fetch->totalBytes | |
357 << " | readyState = " << fetch->readyState; | |
358 } | |
359 #endif | |
360 | |
361 { | |
362 OracleCommandExceptionMessage message | |
363 (context->GetCommand(), Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol)); | |
364 context->EmitMessage(message); | |
365 } | |
366 | |
367 /** | |
368 * TODO - The following code leads to an infinite recursion, at | |
369 * least with Firefox running on incognito mode => WHY? | |
370 **/ | |
371 emscripten_fetch_close(fetch); // Also free data on failure. | |
372 } | |
373 }; | |
374 | |
375 | |
376 | |
377 class WebAssemblyOracle::FetchCommand : public boost::noncopyable | |
378 { | |
379 private: | |
380 WebAssemblyOracle& oracle_; | |
381 boost::weak_ptr<IObserver> receiver_; | |
382 std::unique_ptr<IOracleCommand> command_; | |
383 Orthanc::HttpMethod method_; | |
384 std::string url_; | |
385 std::string body_; | |
386 HttpHeaders headers_; | |
387 unsigned int timeout_; | |
388 std::string expectedContentType_; | |
389 bool hasCredentials_; | |
390 std::string username_; | |
391 std::string password_; | |
392 | |
393 public: | |
394 FetchCommand(WebAssemblyOracle& oracle, | |
395 boost::weak_ptr<IObserver> receiver, | |
396 IOracleCommand* command) : | |
397 oracle_(oracle), | |
398 receiver_(receiver), | |
399 command_(command), | |
400 method_(Orthanc::HttpMethod_Get), | |
401 timeout_(0), | |
402 hasCredentials_(false) | |
403 { | |
404 if (command == NULL) | |
405 { | |
406 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
407 } | |
408 } | |
409 | |
410 void SetMethod(Orthanc::HttpMethod method) | |
411 { | |
412 method_ = method; | |
413 } | |
414 | |
415 void SetUrl(const std::string& url) | |
416 { | |
417 url_ = url; | |
418 } | |
419 | |
420 void SetBody(std::string& body /* will be swapped */) | |
421 { | |
422 body_.swap(body); | |
423 } | |
424 | |
425 void AddHttpHeaders(const HttpHeaders& headers) | |
426 { | |
427 for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); ++it) | |
428 { | |
429 headers_[it->first] = it->second; | |
430 } | |
431 } | |
432 | |
433 void SetTimeout(unsigned int timeout) | |
434 { | |
435 timeout_ = timeout; | |
436 } | |
437 | |
438 void SetCredentials(const std::string& username, | |
439 const std::string& password) | |
440 { | |
441 hasCredentials_ = true; | |
442 username_ = username; | |
443 password_ = password; | |
444 } | |
445 | |
446 void Execute() | |
447 { | |
448 if (command_.get() == NULL) | |
449 { | |
450 // Cannot call Execute() twice | |
451 LOG(ERROR) << "WebAssemblyOracle::Execute(): (command_.get() == NULL)"; | |
452 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
453 } | |
454 | |
455 emscripten_fetch_attr_t attr; | |
456 emscripten_fetch_attr_init(&attr); | |
457 | |
458 const char* method; | |
459 | |
460 switch (method_) | |
461 { | |
462 case Orthanc::HttpMethod_Get: | |
463 method = "GET"; | |
464 break; | |
465 | |
466 case Orthanc::HttpMethod_Post: | |
467 method = "POST"; | |
468 break; | |
469 | |
470 case Orthanc::HttpMethod_Delete: | |
471 method = "DELETE"; | |
472 break; | |
473 | |
474 case Orthanc::HttpMethod_Put: | |
475 method = "PUT"; | |
476 break; | |
477 | |
478 default: | |
479 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
480 } | |
481 | |
482 strcpy(attr.requestMethod, method); | |
483 | |
484 attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY | EMSCRIPTEN_FETCH_REPLACE; | |
485 attr.onsuccess = FetchContext::SuccessCallback; | |
486 attr.onerror = FetchContext::FailureCallback; | |
487 attr.timeoutMSecs = timeout_ * 1000; | |
488 | |
489 if (hasCredentials_) | |
490 { | |
491 attr.withCredentials = EM_TRUE; | |
492 attr.userName = username_.c_str(); | |
493 attr.password = password_.c_str(); | |
494 } | |
495 | |
496 std::vector<const char*> headers; | |
497 headers.reserve(2 * headers_.size() + 1); | |
498 | |
499 std::string expectedContentType; | |
500 | |
501 for (HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); ++it) | |
502 { | |
503 std::string key; | |
504 Orthanc::Toolbox::ToLowerCase(key, it->first); | |
505 | |
506 if (key == "accept") | |
507 { | |
508 expectedContentType = it->second; | |
509 } | |
510 | |
511 if (key != "accept-encoding") // Web browsers forbid the modification of this HTTP header | |
512 { | |
513 headers.push_back(it->first.c_str()); | |
514 headers.push_back(it->second.c_str()); | |
515 } | |
516 } | |
517 | |
518 headers.push_back(NULL); // Termination of the array of HTTP headers | |
519 | |
520 attr.requestHeaders = &headers[0]; | |
521 | |
522 char* requestData = NULL; | |
523 if (!body_.empty()) | |
524 requestData = reinterpret_cast<char*>(malloc(body_.size())); | |
525 | |
526 try | |
527 { | |
528 if (!body_.empty()) | |
529 { | |
530 memcpy(requestData, &(body_[0]), body_.size()); | |
531 attr.requestDataSize = body_.size(); | |
532 attr.requestData = requestData; | |
533 } | |
534 attr.userData = new FetchContext(oracle_, receiver_, command_.release(), expectedContentType); | |
535 | |
536 // Must be the last call to prevent memory leak on error | |
537 emscripten_fetch(&attr, url_.c_str()); | |
538 } | |
539 catch(...) | |
540 { | |
541 if(requestData != NULL) | |
542 free(requestData); | |
543 throw; | |
544 } | |
545 } | |
546 }; | |
547 | |
548 | |
549 void WebAssemblyOracle::SetOrthancUrl(FetchCommand& command, | |
550 const std::string& uri) const | |
551 { | |
552 if (isLocalOrthanc_) | |
553 { | |
554 command.SetUrl(localOrthancRoot_ + uri); | |
555 } | |
556 else | |
557 { | |
558 command.SetUrl(remoteOrthanc_.GetUrl() + uri); | |
559 command.AddHttpHeaders(remoteOrthanc_.GetHttpHeaders()); | |
560 | |
561 if (!remoteOrthanc_.GetUsername().empty()) | |
562 { | |
563 command.SetCredentials(remoteOrthanc_.GetUsername(), remoteOrthanc_.GetPassword()); | |
564 } | |
565 } | |
566 } | |
567 | |
568 | |
569 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver, | |
570 HttpCommand* command) | |
571 { | |
572 FetchCommand fetch(*this, receiver, command); | |
573 | |
574 fetch.SetMethod(command->GetMethod()); | |
575 fetch.SetUrl(command->GetUrl()); | |
576 fetch.AddHttpHeaders(command->GetHttpHeaders()); | |
577 fetch.SetTimeout(command->GetTimeout()); | |
578 | |
579 if (command->GetMethod() == Orthanc::HttpMethod_Post || | |
580 command->GetMethod() == Orthanc::HttpMethod_Put) | |
581 { | |
582 std::string body; | |
583 command->SwapBody(body); | |
584 fetch.SetBody(body); | |
585 } | |
586 | |
587 fetch.Execute(); | |
588 } | |
589 | |
590 | |
591 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver, | |
592 OrthancRestApiCommand* command) | |
593 { | |
594 try | |
595 { | |
596 //LOG(TRACE) << "*********** WebAssemblyOracle::Execute."; | |
597 //LOG(TRACE) << "WebAssemblyOracle::Execute | command = " << command; | |
598 FetchCommand fetch(*this, receiver, command); | |
599 | |
600 fetch.SetMethod(command->GetMethod()); | |
601 SetOrthancUrl(fetch, command->GetUri()); | |
602 fetch.AddHttpHeaders(command->GetHttpHeaders()); | |
603 fetch.SetTimeout(command->GetTimeout()); | |
604 | |
605 if (command->GetMethod() == Orthanc::HttpMethod_Post || | |
606 command->GetMethod() == Orthanc::HttpMethod_Put) | |
607 { | |
608 std::string body; | |
609 command->SwapBody(body); | |
610 fetch.SetBody(body); | |
611 } | |
612 | |
613 fetch.Execute(); | |
614 //LOG(TRACE) << "*********** successful end of WebAssemblyOracle::Execute."; | |
615 } | |
616 catch (const Orthanc::OrthancException& e) | |
617 { | |
618 if (e.HasDetails()) | |
619 { | |
620 LOG(ERROR) << "OrthancException in WebAssemblyOracle::Execute: " << e.What() << " Details: " << e.GetDetails(); | |
621 } | |
622 else | |
623 { | |
624 LOG(ERROR) << "OrthancException in WebAssemblyOracle::Execute: " << e.What(); | |
625 } | |
626 //LOG(TRACE) << "*********** failing end of WebAssemblyOracle::Execute."; | |
627 throw; | |
628 } | |
629 catch (const std::exception& e) | |
630 { | |
631 LOG(ERROR) << "std::exception in WebAssemblyOracle::Execute: " << e.what(); | |
632 // LOG(TRACE) << "*********** failing end of WebAssemblyOracle::Execute."; | |
633 throw; | |
634 } | |
635 catch (...) | |
636 { | |
637 LOG(ERROR) << "Unknown exception in WebAssemblyOracle::Execute"; | |
638 // LOG(TRACE) << "*********** failing end of WebAssemblyOracle::Execute."; | |
639 throw; | |
640 } | |
641 } | |
642 | |
643 | |
644 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver, | |
645 GetOrthancImageCommand* command) | |
646 { | |
647 FetchCommand fetch(*this, receiver, command); | |
648 | |
649 SetOrthancUrl(fetch, command->GetUri()); | |
650 fetch.AddHttpHeaders(command->GetHttpHeaders()); | |
651 fetch.SetTimeout(command->GetTimeout()); | |
652 | |
653 fetch.Execute(); | |
654 } | |
655 | |
656 | |
657 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver, | |
658 GetOrthancWebViewerJpegCommand* command) | |
659 { | |
660 FetchCommand fetch(*this, receiver, command); | |
661 | |
662 SetOrthancUrl(fetch, command->GetUri()); | |
663 fetch.AddHttpHeaders(command->GetHttpHeaders()); | |
664 fetch.SetTimeout(command->GetTimeout()); | |
665 | |
666 fetch.Execute(); | |
667 } | |
668 | |
669 | |
670 void WebAssemblyOracle::Execute(boost::weak_ptr<IObserver> receiver, | |
671 ParseDicomFromWadoCommand* command) | |
672 { | |
673 std::unique_ptr<ParseDicomFromWadoCommand> protection(command); | |
674 | |
675 #if ORTHANC_ENABLE_DCMTK == 1 | |
676 if (dicomCache_.get()) | |
677 { | |
678 ParsedDicomCache::Reader reader(*dicomCache_, BUCKET_SOP, protection->GetSopInstanceUid()); | |
679 if (reader.IsValid() && | |
680 reader.HasPixelData()) | |
681 { | |
682 // Reuse the DICOM file from the cache | |
683 ParseDicomSuccessMessage message(*protection, protection->GetSource(), reader.GetDicom(), | |
684 reader.GetFileSize(), reader.HasPixelData()); | |
685 EmitMessage(receiver, message); | |
686 return; | |
687 } | |
688 } | |
689 #endif | |
690 | |
691 switch (command->GetRestCommand().GetType()) | |
692 { | |
693 case IOracleCommand::Type_Http: | |
694 { | |
695 const HttpCommand& rest = | |
696 dynamic_cast<const HttpCommand&>(protection->GetRestCommand()); | |
697 | |
698 FetchCommand fetch(*this, receiver, protection.release()); | |
699 | |
700 fetch.SetMethod(rest.GetMethod()); | |
701 fetch.SetUrl(rest.GetUrl()); | |
702 fetch.AddHttpHeaders(rest.GetHttpHeaders()); | |
703 fetch.SetTimeout(rest.GetTimeout()); | |
704 | |
705 if (rest.GetMethod() == Orthanc::HttpMethod_Post || | |
706 rest.GetMethod() == Orthanc::HttpMethod_Put) | |
707 { | |
708 std::string body = rest.GetBody(); | |
709 fetch.SetBody(body); | |
710 } | |
711 | |
712 fetch.Execute(); | |
713 break; | |
714 } | |
715 | |
716 case IOracleCommand::Type_OrthancRestApi: | |
717 { | |
718 const OrthancRestApiCommand& rest = | |
719 dynamic_cast<const OrthancRestApiCommand&>(protection->GetRestCommand()); | |
720 | |
721 FetchCommand fetch(*this, receiver, protection.release()); | |
722 | |
723 fetch.SetMethod(rest.GetMethod()); | |
724 SetOrthancUrl(fetch, rest.GetUri()); | |
725 fetch.AddHttpHeaders(rest.GetHttpHeaders()); | |
726 fetch.SetTimeout(rest.GetTimeout()); | |
727 | |
728 if (rest.GetMethod() == Orthanc::HttpMethod_Post || | |
729 rest.GetMethod() == Orthanc::HttpMethod_Put) | |
730 { | |
731 std::string body = rest.GetBody(); | |
732 fetch.SetBody(body); | |
733 } | |
734 | |
735 fetch.Execute(); | |
736 break; | |
737 } | |
738 | |
739 default: | |
740 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
741 } | |
742 } | |
743 | |
744 | |
745 bool WebAssemblyOracle::Schedule(boost::shared_ptr<IObserver> receiver, | |
746 IOracleCommand* command) | |
747 { | |
748 LOG(TRACE) << "WebAssemblyOracle::Schedule : receiver = " | |
749 << std::hex << receiver.get(); | |
750 | |
751 std::unique_ptr<IOracleCommand> protection(command); | |
752 | |
753 if (command == NULL) | |
754 { | |
755 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
756 } | |
757 | |
758 switch (command->GetType()) | |
759 { | |
760 case IOracleCommand::Type_Http: | |
761 Execute(receiver, dynamic_cast<HttpCommand*>(protection.release())); | |
762 break; | |
763 | |
764 case IOracleCommand::Type_OrthancRestApi: | |
765 Execute(receiver, dynamic_cast<OrthancRestApiCommand*>(protection.release())); | |
766 break; | |
767 | |
768 case IOracleCommand::Type_GetOrthancImage: | |
769 Execute(receiver, dynamic_cast<GetOrthancImageCommand*>(protection.release())); | |
770 break; | |
771 | |
772 case IOracleCommand::Type_GetOrthancWebViewerJpeg: | |
773 Execute(receiver, dynamic_cast<GetOrthancWebViewerJpegCommand*>(protection.release())); | |
774 break; | |
775 | |
776 case IOracleCommand::Type_Sleep: | |
777 { | |
778 unsigned int timeoutMS = dynamic_cast<SleepOracleCommand*>(command)->GetDelay(); | |
779 emscripten_set_timeout(TimeoutContext::Callback, timeoutMS, | |
780 new TimeoutContext(*this, receiver, protection.release())); | |
781 break; | |
782 } | |
783 | |
784 case IOracleCommand::Type_ParseDicomFromWado: | |
785 #if ORTHANC_ENABLE_DCMTK == 1 | |
786 Execute(receiver, dynamic_cast<ParseDicomFromWadoCommand*>(protection.release())); | |
787 #else | |
788 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, | |
789 "DCMTK must be enabled to parse DICOM files"); | |
790 #endif | |
791 break; | |
792 | |
793 default: | |
794 LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in Schedule): " | |
795 << command->GetType(); | |
796 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
797 } | |
798 | |
799 return true; | |
800 } | |
801 | |
802 | |
803 void WebAssemblyOracle::SetDicomCacheSize(size_t size) | |
804 { | |
805 #if ORTHANC_ENABLE_DCMTK == 1 | |
806 if (size == 0) | |
807 { | |
808 dicomCache_.reset(); | |
809 } | |
810 else | |
811 { | |
812 dicomCache_.reset(new ParsedDicomCache(size)); | |
813 } | |
814 #else | |
815 LOG(INFO) << "DCMTK support is disabled, the DICOM cache is disabled"; | |
816 #endif | |
817 } | |
818 } |