comparison OrthancStone/Sources/Oracle/WebAssemblyOracle.cpp @ 1512:244ad1e4e76a

reorganization of folders
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 07 Jul 2020 16:21:02 +0200
parents Framework/Oracle/WebAssemblyOracle.cpp@121d01aa328e
children 5bc305a166c2
comparison
equal deleted inserted replaced
1511:9dfeee74c1e6 1512:244ad1e4e76a
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 #include "WebAssemblyOracle.h"
23
24 #include "OracleCommandExceptionMessage.h"
25 #include "SleepOracleCommand.h"
26
27 #if ORTHANC_ENABLE_DCMTK == 1
28 # include "ParseDicomSuccessMessage.h"
29 static unsigned int BUCKET_SOP = 1;
30 #endif
31
32 #include "GetOrthancImageCommand.h"
33 #include "GetOrthancWebViewerJpegCommand.h"
34 #include "HttpCommand.h"
35 #include "OrthancRestApiCommand.h"
36 #include "ParseDicomFromWadoCommand.h"
37
38 #include <OrthancException.h>
39 #include <Toolbox.h>
40
41 #include <emscripten.h>
42 #include <emscripten/html5.h>
43 #include <emscripten/fetch.h>
44
45 namespace OrthancStone
46 {
47 class WebAssemblyOracle::TimeoutContext
48 {
49 private:
50 WebAssemblyOracle& oracle_;
51 boost::weak_ptr<IObserver> receiver_;
52 std::unique_ptr<SleepOracleCommand> command_;
53
54 public:
55 TimeoutContext(WebAssemblyOracle& oracle,
56 boost::weak_ptr<IObserver> receiver,
57 IOracleCommand* command) :
58 oracle_(oracle),
59 receiver_(receiver)
60 {
61 if (command == NULL)
62 {
63 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
64 }
65 else
66 {
67 command_.reset(dynamic_cast<SleepOracleCommand*>(command));
68 }
69 }
70
71 void EmitMessage()
72 {
73 assert(command_.get() != NULL);
74
75 SleepOracleCommand::TimeoutMessage message(*command_);
76 oracle_.EmitMessage(receiver_, message);
77 }
78
79 static void Callback(void *userData)
80 {
81 std::unique_ptr<TimeoutContext> context(reinterpret_cast<TimeoutContext*>(userData));
82 context->EmitMessage();
83 }
84 };
85
86
87 /**
88 This object is created on the heap for every http request.
89 It is deleted in the success (or error) callbacks.
90
91 This object references the receiver of the request. Since this is a raw
92 reference, we need additional checks to make sure we send the response to
93 the same object, for the object can be deleted and a new one recreated at the
94 same address (it often happens in the [single-threaded] browser context).
95 */
96 class WebAssemblyOracle::FetchContext : public boost::noncopyable
97 {
98 private:
99 WebAssemblyOracle& oracle_;
100 boost::weak_ptr<IObserver> receiver_;
101 std::unique_ptr<IOracleCommand> command_;
102 std::string expectedContentType_;
103
104 public:
105 FetchContext(WebAssemblyOracle& oracle,
106 boost::weak_ptr<IObserver> receiver,
107 IOracleCommand* command,
108 const std::string& expectedContentType) :
109 oracle_(oracle),
110 receiver_(receiver),
111 command_(command),
112 expectedContentType_(expectedContentType)
113 {
114 if (Orthanc::Logging::IsTraceLevelEnabled())
115 {
116 // Calling "receiver.lock()" is expensive, hence the quick check if TRACE is enabled
117 LOG(TRACE) << "WebAssemblyOracle::FetchContext::FetchContext() | "
118 << "receiver address = " << std::hex << receiver.lock().get();
119 }
120
121 if (command == NULL)
122 {
123 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
124 }
125 }
126
127 const std::string& GetExpectedContentType() const
128 {
129 return expectedContentType_;
130 }
131
132 IMessageEmitter& GetEmitter() const
133 {
134 return oracle_;
135 }
136
137 boost::weak_ptr<IObserver> GetReceiver() const
138 {
139 return receiver_;
140 }
141
142 void EmitMessage(const IMessage& message)
143 {
144 if (Orthanc::Logging::IsTraceLevelEnabled())
145 {
146 // Calling "receiver_.lock()" is expensive, hence the quick check if TRACE is enabled
147 LOG(TRACE) << "WebAssemblyOracle::FetchContext::EmitMessage receiver_ = "
148 << std::hex << receiver_.lock().get() << std::dec;
149 }
150
151 oracle_.EmitMessage(receiver_, message);
152 }
153
154 IOracleCommand& GetCommand() const
155 {
156 return *command_;
157 }
158
159 template <typename T>
160 const T& GetTypedCommand() const
161 {
162 return dynamic_cast<T&>(*command_);
163 }
164
165 #if ORTHANC_ENABLE_DCMTK == 1
166 void StoreInCache(const std::string& sopInstanceUid,
167 std::unique_ptr<Orthanc::ParsedDicomFile>& dicom,
168 size_t fileSize)
169 {
170 if (oracle_.dicomCache_.get())
171 {
172 // Store it into the cache for future use
173 oracle_.dicomCache_->Acquire(BUCKET_SOP, sopInstanceUid,
174 dicom.release(), fileSize, true);
175 }
176 }
177 #endif
178
179 static void SuccessCallback(emscripten_fetch_t *fetch)
180 {
181 /**
182 * Firstly, make a local copy of the fetched information, and
183 * free data associated with the fetch.
184 **/
185
186 if (fetch->userData == NULL)
187 {
188 LOG(ERROR) << "WebAssemblyOracle::FetchContext::SuccessCallback fetch->userData is NULL!!!!!!!";
189 return;
190 }
191
192 std::unique_ptr<FetchContext> context(reinterpret_cast<FetchContext*>(fetch->userData));
193
194 std::string answer;
195 if (fetch->numBytes > 0)
196 {
197 answer.assign(fetch->data, fetch->numBytes);
198 }
199
200
201 /**
202 * Retrieving the headers of the HTTP answer.
203 **/
204 HttpHeaders headers;
205
206 #if (__EMSCRIPTEN_major__ < 1 || \
207 (__EMSCRIPTEN_major__ == 1 && __EMSCRIPTEN_minor__ < 38) || \
208 (__EMSCRIPTEN_major__ == 1 && __EMSCRIPTEN_minor__ == 38 && __EMSCRIPTEN_tiny__ < 37))
209 # warning Consider upgrading Emscripten to a version above 1.38.37, incomplete support of Fetch API
210
211 /**
212 * HACK - If emscripten < 1.38.37, the fetch API does not
213 * contain a way to retrieve the HTTP headers of the answer. We
214 * make the assumption that the "Content-Type" header of the
215 * response is the same as the "Accept" header of the
216 * query. This is fixed thanks to the
217 * "emscripten_fetch_get_response_headers()" function that was
218 * added to "fetch.h" at emscripten-1.38.37 on 2019-06-26.
219 *
220 * https://github.com/emscripten-core/emscripten/blob/1.38.37/system/include/emscripten/fetch.h
221 * https://github.com/emscripten-core/emscripten/pull/8486
222 **/
223 if (fetch->userData != NULL)
224 {
225 if (!context->GetExpectedContentType().empty())
226 {
227 headers["Content-Type"] = context->GetExpectedContentType();
228 }
229 }
230 #else
231 {
232 size_t size = emscripten_fetch_get_response_headers_length(fetch);
233
234 std::string plainHeaders(size + 1, '\0');
235 emscripten_fetch_get_response_headers(fetch, &plainHeaders[0], size + 1);
236
237 std::vector<std::string> tokens;
238 Orthanc::Toolbox::TokenizeString(tokens, plainHeaders, '\n');
239
240 for (size_t i = 0; i < tokens.size(); i++)
241 {
242 size_t p = tokens[i].find(':');
243 if (p != std::string::npos)
244 {
245 std::string key = Orthanc::Toolbox::StripSpaces(tokens[i].substr(0, p));
246 std::string value = Orthanc::Toolbox::StripSpaces(tokens[i].substr(p + 1));
247 headers[key] = value;
248 }
249 }
250 }
251 #endif
252
253 LOG(TRACE) << "About to call emscripten_fetch_close";
254 emscripten_fetch_close(fetch);
255 LOG(TRACE) << "Successfully called emscripten_fetch_close";
256
257 /**
258 * Secondly, use the retrieved data.
259 * IMPORTANT NOTE: the receiver might be dead. This is prevented
260 * by the object responsible for zombie check, later on.
261 **/
262 try
263 {
264 if (context.get() == NULL)
265 {
266 LOG(ERROR) << "WebAssemblyOracle::FetchContext::SuccessCallback: (context.get() == NULL)";
267 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
268 }
269 else
270 {
271 switch (context->GetCommand().GetType())
272 {
273 case IOracleCommand::Type_Http:
274 {
275 HttpCommand::SuccessMessage message(context->GetTypedCommand<HttpCommand>(), headers, answer);
276 context->EmitMessage(message);
277 break;
278 }
279
280 case IOracleCommand::Type_OrthancRestApi:
281 {
282 LOG(TRACE) << "WebAssemblyOracle::FetchContext::SuccessCallback. About to call context->EmitMessage(message);";
283 OrthancRestApiCommand::SuccessMessage message
284 (context->GetTypedCommand<OrthancRestApiCommand>(), headers, answer);
285 context->EmitMessage(message);
286 break;
287 }
288
289 case IOracleCommand::Type_GetOrthancImage:
290 {
291 context->GetTypedCommand<GetOrthancImageCommand>().ProcessHttpAnswer
292 (context->GetReceiver(), context->GetEmitter(), answer, headers);
293 break;
294 }
295
296 case IOracleCommand::Type_GetOrthancWebViewerJpeg:
297 {
298 context->GetTypedCommand<GetOrthancWebViewerJpegCommand>().ProcessHttpAnswer
299 (context->GetReceiver(), context->GetEmitter(), answer);
300 break;
301 }
302
303 case IOracleCommand::Type_ParseDicomFromWado:
304 {
305 #if ORTHANC_ENABLE_DCMTK == 1
306 const ParseDicomFromWadoCommand& command =
307 context->GetTypedCommand<ParseDicomFromWadoCommand>();
308
309 size_t fileSize;
310 std::unique_ptr<Orthanc::ParsedDicomFile> dicom
311 (ParseDicomSuccessMessage::ParseWadoAnswer(fileSize, answer, headers));
312
313 {
314 ParseDicomSuccessMessage message(command, command.GetSource(), *dicom, fileSize, true);
315 context->EmitMessage(message);
316 }
317
318 context->StoreInCache(command.GetSopInstanceUid(), dicom, fileSize);
319 #else
320 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
321 #endif
322 break;
323 }
324
325 default:
326 LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in SuccessCallback): "
327 << context->GetCommand().GetType();
328 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
329 }
330 }
331 }
332 catch (Orthanc::OrthancException& e)
333 {
334 LOG(INFO) << "Error while processing a fetch answer in the oracle: " << e.What();
335
336 {
337 OracleCommandExceptionMessage message(context->GetCommand(), e);
338 context->EmitMessage(message);
339 }
340 }
341 }
342
343 static void FailureCallback(emscripten_fetch_t *fetch)
344 {
345 std::unique_ptr<FetchContext> context(reinterpret_cast<FetchContext*>(fetch->userData));
346
347 {
348 const size_t kEmscriptenStatusTextSize = sizeof(emscripten_fetch_t::statusText);
349 char message[kEmscriptenStatusTextSize + 1];
350 memcpy(message, fetch->statusText, kEmscriptenStatusTextSize);
351 message[kEmscriptenStatusTextSize] = 0;
352
353 /*LOG(ERROR) << "Fetching " << fetch->url
354 << " failed, HTTP failure status code: " << fetch->status
355 << " | statusText = " << message
356 << " | numBytes = " << fetch->numBytes
357 << " | totalBytes = " << fetch->totalBytes
358 << " | readyState = " << fetch->readyState;*/
359 }
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 break;
774
775 case IOracleCommand::Type_Sleep:
776 {
777 unsigned int timeoutMS = dynamic_cast<SleepOracleCommand*>(command)->GetDelay();
778 emscripten_set_timeout(TimeoutContext::Callback, timeoutMS,
779 new TimeoutContext(*this, receiver, protection.release()));
780 break;
781 }
782
783 case IOracleCommand::Type_ParseDicomFromWado:
784 #if ORTHANC_ENABLE_DCMTK == 1
785 Execute(receiver, dynamic_cast<ParseDicomFromWadoCommand*>(protection.release()));
786 #else
787 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented,
788 "DCMTK must be enabled to parse DICOM files");
789 #endif
790 break;
791
792 default:
793 LOG(ERROR) << "Command type not implemented by the WebAssembly Oracle (in Schedule): "
794 << command->GetType();
795 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
796 }
797
798 return true;
799 }
800
801
802 void WebAssemblyOracle::SetDicomCacheSize(size_t size)
803 {
804 #if ORTHANC_ENABLE_DCMTK == 1
805 if (size == 0)
806 {
807 dicomCache_.reset();
808 }
809 else
810 {
811 dicomCache_.reset(new ParsedDicomCache(size));
812 }
813 #else
814 LOG(INFO) << "DCMTK support is disabled, the DICOM cache is disabled";
815 #endif
816 }
817 }