changeset 417:aee3d7941c9b

preparing to load images using DICOMweb
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 15 Nov 2018 17:28:15 +0100
parents c0589c3173fd
children c23df8b3433b
files Applications/Generic/NativeStoneApplicationRunner.cpp Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp Applications/Samples/SimpleViewer/SimpleViewerApplication.h Applications/Samples/SimpleViewerApplicationSingleFile.h Applications/Samples/SingleFrameApplication.h Applications/Samples/SingleFrameEditorApplication.h Applications/StoneApplicationContext.cpp Applications/StoneApplicationContext.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Toolbox/IWebService.h Framework/Toolbox/OrthancApiClient.cpp Framework/Toolbox/OrthancApiClient.h Framework/Toolbox/OrthancSlicesLoader.cpp Platforms/Generic/OracleWebService.h Platforms/Generic/WebServiceCommandBase.cpp Platforms/Generic/WebServiceCommandBase.h Platforms/Generic/WebServiceDeleteCommand.cpp Platforms/Generic/WebServiceDeleteCommand.h Platforms/Generic/WebServiceGetCommand.cpp Platforms/Generic/WebServiceGetCommand.h Platforms/Generic/WebServicePostCommand.cpp Platforms/Generic/WebServicePostCommand.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/WasmWebService.cpp Platforms/Wasm/WasmWebService.h Platforms/Wasm/WasmWebService.js
diffstat 27 files changed, 251 insertions(+), 155 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/Generic/NativeStoneApplicationRunner.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -193,7 +193,7 @@
 
         {
           OracleWebService webService(broker_, oracle, webServiceParameters, context);
-          context.SetWebService(webService);
+          context.Initialize(broker_, webService, webServiceParameters.GetUrl());
 
           application_.Initialize(&context, statusBar, parameters);
 
--- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -61,10 +61,8 @@
       mainLayout_->AddWidget(thumbnailsLayout_);
       mainLayout_->AddWidget(mainWidget_);
 
-      orthancApiClient_.reset(new OrthancApiClient(IObserver::GetBroker(), context_->GetWebService()));
-
       // sources
-      smartLoader_.reset(new SmartLoader(IObserver::GetBroker(), *orthancApiClient_));
+      smartLoader_.reset(new SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient()));
       smartLoader_->SetImageQuality(SliceImageQuality_FullPam);
 
       mainLayout_->SetTransmitMouseOver(true);
@@ -80,7 +78,7 @@
     if (parameters.count("studyId") < 1)
     {
       LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
-      orthancApiClient_->GetJsonAsync("/studies", new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived));
+      context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived));
     }
     else
     {
@@ -118,7 +116,7 @@
     {
       for (size_t i=0; i < response["Series"].size(); i++)
       {
-        orthancApiClient_->GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived));
+        context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived));
       }
     }
   }
@@ -168,7 +166,7 @@
 
   void SimpleViewerApplication::SelectStudy(const std::string& studyId)
   {
-    orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
+    context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
   }
 
   void SimpleViewerApplication::OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message)
--- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.h	Thu Nov 15 17:28:15 2018 +0100
@@ -104,7 +104,6 @@
 
     IStatusBar*                         statusBar_;
     std::auto_ptr<SmartLoader>          smartLoader_;
-    std::auto_ptr<OrthancApiClient>     orthancApiClient_;
 
     Orthanc::Font                       font_;
 
--- a/Applications/Samples/SimpleViewerApplicationSingleFile.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h	Thu Nov 15 17:28:15 2018 +0100
@@ -247,7 +247,6 @@
 
       IStatusBar*                          statusBar_;
       std::auto_ptr<SmartLoader>           smartLoader_;
-      std::auto_ptr<OrthancApiClient>      orthancApiClient_;
 
       Orthanc::Font                        font_;
 
@@ -304,10 +303,8 @@
           mainLayout_->AddWidget(thumbnailsLayout_);
           mainLayout_->AddWidget(mainWidget_);
 
-          orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService()));
-
           // sources
-          smartLoader_.reset(new SmartLoader(GetBroker(), *orthancApiClient_));
+          smartLoader_.reset(new SmartLoader(GetBroker(), context->GetOrthancApiClient()));
           smartLoader_->SetImageQuality(SliceImageQuality_FullPam);
 
           mainLayout_->SetTransmitMouseOver(true);
@@ -323,7 +320,7 @@
         if (parameters.count("studyId") < 1)
         {
           LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
-          orthancApiClient_->GetJsonAsync(
+          context->GetOrthancApiClient().GetJsonAsync(
             "/studies",
             new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>
             (*this, &SimpleViewerApplication::OnStudyListReceived));
@@ -353,7 +350,7 @@
         {
           for (size_t i=0; i < response["Series"].size(); i++)
           {
-            orthancApiClient_->GetJsonAsync(
+            context_->GetOrthancApiClient().GetJsonAsync(
               "/series/" + response["Series"][(int)i].asString(),
               new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>
               (*this, &SimpleViewerApplication::OnSeriesReceived));
@@ -405,7 +402,9 @@
       void SelectStudy(const std::string& studyId)
       {
         LOG(INFO) << "Selecting study: " << studyId;
-        orthancApiClient_->GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
+        context_->GetOrthancApiClient().GetJsonAsync(
+          "/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>
+          (*this, &SimpleViewerApplication::OnStudyReceived));
       }
 
       void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message)
--- a/Applications/Samples/SingleFrameApplication.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/Samples/SingleFrameApplication.h	Thu Nov 15 17:28:15 2018 +0100
@@ -197,7 +197,6 @@
       }
       
       std::auto_ptr<Interactor>         mainWidgetInteractor_;
-      std::auto_ptr<OrthancApiClient>   orthancApiClient_;
       const DicomSeriesVolumeSlicer*    source_;
       unsigned int                      slice_;
 
@@ -243,10 +242,9 @@
         std::string instance = parameters["instance"].as<std::string>();
         int frame = parameters["frame"].as<unsigned int>();
 
-        orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService()));
         mainWidget_ = new SliceViewerWidget(GetBroker(), "main-widget");
 
-        std::auto_ptr<DicomSeriesVolumeSlicer> layer(new DicomSeriesVolumeSlicer(GetBroker(), *orthancApiClient_));
+        std::auto_ptr<DicomSeriesVolumeSlicer> layer(new DicomSeriesVolumeSlicer(GetBroker(), context->GetOrthancApiClient()));
         source_ = layer.get();
         layer->LoadFrame(instance, frame);
         layer->RegisterObserverCallback(new Callable<SingleFrameApplication, IVolumeSlicer::GeometryReadyMessage>(*this, &SingleFrameApplication::OnMainWidgetGeometryReady));
--- a/Applications/Samples/SingleFrameEditorApplication.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Thu Nov 15 17:28:15 2018 +0100
@@ -61,8 +61,9 @@
       };
         
 
-      UndoRedoStack      undoRedoStack_;
-      Tool               tool_;
+      StoneApplicationContext*  context_;
+      UndoRedoStack             undoRedoStack_;
+      Tool                      tool_;
 
 
       static double GetHandleSize()
@@ -74,9 +75,15 @@
     public:
       RadiographyEditorInteractor(MessageBroker& broker) :
         IObserver(broker),
+        context_(NULL),
         tool_(Tool_Move)
       {
       }
+
+      void SetContext(StoneApplicationContext& context)
+      {
+        context_ = &context;
+      }
     
       virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& worldWidget,
                                                           const ViewportGeometry& view,
@@ -292,8 +299,13 @@
             tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
             tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
 
-            widget.GetScene().ExportDicom(tags, 0.1, 0.1, widget.IsInverted(),
-                                          widget.GetInterpolation(), EXPORT_USING_PAM);
+            if (context_ != NULL)
+            {
+              widget.GetScene().ExportDicom(context_->GetOrthancApiClient(),
+                                            tags, 0.1, 0.1, widget.IsInverted(),
+                                            widget.GetInterpolation(), EXPORT_USING_PAM);
+            }
+            
             break;
           }
 
@@ -367,7 +379,6 @@
       public IObserver
     {
     private:
-      std::auto_ptr<OrthancApiClient>  orthancApiClient_;
       std::auto_ptr<RadiographyScene>  scene_;
       RadiographyEditorInteractor      interactor_;
 
@@ -403,6 +414,7 @@
         using namespace OrthancStone;
 
         context_ = context;
+        interactor_.SetContext(*context);
 
         statusBar.SetMessage("Use the key \"a\" to reinitialize the layout");
         statusBar.SetMessage("Use the key \"c\" to crop");
@@ -427,15 +439,14 @@
         std::string instance = parameters["instance"].as<std::string>();
         int frame = parameters["frame"].as<unsigned int>();
 
-        orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService()));
-
         Orthanc::FontRegistry fonts;
         fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
         
-        scene_.reset(new RadiographyScene(GetBroker(), *orthancApiClient_));
-        scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
-        //scene_->LoadDicomFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false);
-
+        scene_.reset(new RadiographyScene(GetBroker()));
+        //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
+        scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false);
+        //scene_->LoadDicomWebFrame(context->GetWebService());
+        
         {
           RadiographyLayer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld");
           layer.SetResizeable(true);
--- a/Applications/StoneApplicationContext.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/StoneApplicationContext.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -21,6 +21,35 @@
 
 #include "StoneApplicationContext.h"
 
+#include <Core/OrthancException.h>
+
 namespace OrthancStone
 {
+  IWebService& StoneApplicationContext::GetWebService()
+  {
+    if (webService_ == NULL)
+    {
+      throw Orthanc::ErrorCode_BadSequenceOfCalls;
+    }
+    
+    return *webService_;
+  }
+
+  OrthancApiClient& StoneApplicationContext::GetOrthancApiClient()
+  {
+    if (orthanc_.get() == NULL)
+    {
+      throw Orthanc::ErrorCode_BadSequenceOfCalls;
+    }
+    
+    return *orthanc_;
+  }
+
+  void StoneApplicationContext::Initialize(MessageBroker& broker,
+                                           IWebService& webService,
+                                           const std::string& orthancBaseUrl)
+  {
+    webService_ = &webService;
+    orthanc_.reset(new OrthancApiClient(broker, webService, orthancBaseUrl));
+  }
 }
--- a/Applications/StoneApplicationContext.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Applications/StoneApplicationContext.h	Thu Nov 15 17:28:15 2018 +0100
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "../Framework/Toolbox/IWebService.h"
+#include "../Framework/Toolbox/OrthancApiClient.h"
 #include "../Framework/Viewport/WidgetViewport.h"
 
 #include <list>
@@ -34,25 +35,31 @@
   // the StoneApplication can perform HTTP requests.  In a WASM environment,
   // the WebService is provided by the browser while, in a native environment,
   // the WebService is provided by the OracleWebService (a C++ Http client)
+
   class StoneApplicationContext : public boost::noncopyable
   {
+  protected:
+    // TODO ADD THE MessageBroker HERE
+    
+    IWebService*                     webService_;
+    std::auto_ptr<OrthancApiClient>  orthanc_;
 
-  protected:
-    IWebService* webService_;
   public:
-    StoneApplicationContext()
-      : webService_(NULL)
+    StoneApplicationContext() :
+      webService_(NULL)
     {
     }
 
-    IWebService& GetWebService() {return *webService_;}
-    void SetWebService(IWebService& webService)
-    {
-      webService_ = &webService;
-    }
-
     virtual ~StoneApplicationContext()
     {
     }
+
+    IWebService& GetWebService();
+
+    OrthancApiClient& GetOrthancApiClient();
+
+    void Initialize(MessageBroker& broker,
+                    IWebService& webService,
+                    const std::string& orthancBaseUrl);
   };
 }
--- a/Framework/Radiography/RadiographyScene.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Framework/Radiography/RadiographyScene.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -423,11 +423,9 @@
   }
     
 
-  RadiographyScene::RadiographyScene(MessageBroker& broker,
-                                     OrthancApiClient& orthanc) :
+  RadiographyScene::RadiographyScene(MessageBroker& broker) :
     IObserver(broker),
     IObservable(broker),
-    orthanc_(orthanc),
     countLayers_(0),
     hasWindowing_(false),
     windowingCenter_(0),  // Dummy initialization
@@ -523,17 +521,18 @@
   }
 
     
-  RadiographyLayer& RadiographyScene::LoadDicomFrame(const std::string& instance,
+  RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc,
+                                                     const std::string& instance,
                                                      unsigned int frame,
                                                      bool httpCompression)
   {
     RadiographyLayer& layer = RegisterLayer(new DicomLayer);
 
     {
-      IWebService::Headers headers;
+      IWebService::HttpHeaders headers;
       std::string uri = "/instances/" + instance + "/tags";
         
-      orthanc_.GetBinaryAsync(
+      orthanc.GetBinaryAsync(
         uri, headers,
         new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
         (*this, &RadiographyScene::OnTagsReceived), NULL,
@@ -541,7 +540,7 @@
     }
 
     {
-      IWebService::Headers headers;
+      IWebService::HttpHeaders headers;
       headers["Accept"] = "image/x-portable-arbitrarymap";
 
       if (httpCompression)
@@ -552,7 +551,7 @@
       std::string uri = ("/instances/" + instance + "/frames/" +
                          boost::lexical_cast<std::string>(frame) + "/image-uint16");
         
-      orthanc_.GetBinaryAsync(
+      orthanc.GetBinaryAsync(
         uri, headers,
         new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
         (*this, &RadiographyScene::OnFrameReceived), NULL,
@@ -562,6 +561,16 @@
     return layer;
   }
 
+
+  RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web)
+  {
+    RadiographyLayer& layer = RegisterLayer(new DicomLayer);
+
+      
+    return layer;
+  }
+
+
     
   void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
   {
@@ -728,7 +737,8 @@
 
   // Export using PAM is faster than using PNG, but requires Orthanc
   // core >= 1.4.3
-  void RadiographyScene::ExportDicom(const Orthanc::DicomMap& dicom,
+  void RadiographyScene::ExportDicom(OrthancApiClient& orthanc,
+                                     const Orthanc::DicomMap& dicom,
                                      double pixelSpacingX,
                                      double pixelSpacingY,
                                      bool invert,
@@ -830,7 +840,7 @@
                        std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
                        ";base64," + base64);
 
-    orthanc_.PostJsonAsyncExpectJson(
+    orthanc.PostJsonAsyncExpectJson(
       "/tools/create-dicom", json,
       new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage>
       (*this, &RadiographyScene::OnDicomExported),
@@ -840,7 +850,14 @@
 
   void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
   {
-    LOG(INFO) << "DICOM export was successful:"
+    LOG(INFO) << "DICOM export was successful: "
               << message.GetJson().toStyledString();
   }
+
+
+  void RadiographyScene::OnDicomWebReceived(const IWebService::HttpRequestSuccessMessage& message)
+  {
+    LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes";
+  }
+
 }
--- a/Framework/Radiography/RadiographyScene.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Framework/Radiography/RadiographyScene.h	Thu Nov 15 17:28:15 2018 +0100
@@ -74,12 +74,11 @@
 
     typedef std::map<size_t, RadiographyLayer*>  Layers;
         
-    OrthancApiClient&  orthanc_;
-    size_t             countLayers_;
-    bool               hasWindowing_;
-    float              windowingCenter_;
-    float              windowingWidth_;
-    Layers             layers_;
+    size_t  countLayers_;
+    bool    hasWindowing_;
+    float   windowingCenter_;
+    float   windowingWidth_;
+    Layers  layers_;
 
     RadiographyLayer& RegisterLayer(RadiographyLayer* layer);
 
@@ -89,9 +88,10 @@
     
     void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message);
 
+    void OnDicomWebReceived(const IWebService::HttpRequestSuccessMessage& message);
+
   public:
-    RadiographyScene(MessageBroker& broker,
-                     OrthancApiClient& orthanc);
+    RadiographyScene(MessageBroker& broker);
     
     virtual ~RadiographyScene();
 
@@ -110,10 +110,13 @@
     RadiographyLayer& LoadTestBlock(unsigned int width,
                                     unsigned int height);
     
-    RadiographyLayer& LoadDicomFrame(const std::string& instance,
+    RadiographyLayer& LoadDicomFrame(OrthancApiClient& orthanc,
+                                     const std::string& instance,
                                      unsigned int frame,
                                      bool httpCompression);
 
+    RadiographyLayer& LoadDicomWebFrame(IWebService& web);
+
     Extent2D GetSceneExtent() const;
 
     void Render(Orthanc::ImageAccessor& buffer,
@@ -133,7 +136,8 @@
 
     // Export using PAM is faster than using PNG, but requires Orthanc
     // core >= 1.4.3
-    void ExportDicom(const Orthanc::DicomMap& dicom,
+    void ExportDicom(OrthancApiClient& orthanc,
+                     const Orthanc::DicomMap& dicom,
                      double pixelSpacingX,
                      double pixelSpacingY,
                      bool invert,
--- a/Framework/Toolbox/IWebService.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Framework/Toolbox/IWebService.h	Thu Nov 15 17:28:15 2018 +0100
@@ -43,7 +43,7 @@
     MessageBroker& broker_;
     
   public:
-    typedef std::map<std::string, std::string> Headers;
+    typedef std::map<std::string, std::string> HttpHeaders;
 
     class HttpRequestSuccessMessage : public BaseMessage<MessageType_HttpRequestSuccess>
     {
@@ -51,16 +51,19 @@
       const std::string&             uri_;
       const void*                    answer_;
       size_t                         answerSize_;
+      const HttpHeaders&             answerHeaders_;
       const Orthanc::IDynamicObject* payload_;
 
     public:
       HttpRequestSuccessMessage(const std::string& uri,
                                 const void* answer,
                                 size_t answerSize,
+                                const HttpHeaders& answerHeaders,
                                 const Orthanc::IDynamicObject* payload) :
         uri_(uri),
         answer_(answer),
         answerSize_(answerSize),
+        answerHeaders_(answerHeaders),
         payload_(payload)
       {
       }
@@ -80,6 +83,11 @@
         return answerSize_;
       }
 
+      const HttpHeaders&  GetAnswerHttpHeaders() const
+      {
+        return answerHeaders_;
+      }
+
       bool HasPayload() const
       {
         return payload_ != NULL;
@@ -129,14 +137,14 @@
 
     
     virtual void GetAsync(const std::string& uri,
-                          const Headers& headers,
+                          const HttpHeaders& headers,
                           Orthanc::IDynamicObject* payload  /* takes ownership */,
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
                           unsigned int timeoutInSeconds = 60) = 0;
 
     virtual void PostAsync(const std::string& uri,
-                           const Headers& headers,
+                           const HttpHeaders& headers,
                            const std::string& body,
                            Orthanc::IDynamicObject* payload  /* takes ownership */,
                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
@@ -144,7 +152,7 @@
                            unsigned int timeoutInSeconds = 60) = 0;
 
     virtual void DeleteAsync(const std::string& uri,
-                             const Headers& headers,
+                             const HttpHeaders& headers,
                              Orthanc::IDynamicObject* payload  /* takes ownership */,
                              MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
                              MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
--- a/Framework/Toolbox/OrthancApiClient.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Framework/Toolbox/OrthancApiClient.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -168,10 +168,12 @@
 
 
   OrthancApiClient::OrthancApiClient(MessageBroker& broker,
-                                     IWebService& orthanc) :
+                                     IWebService& web,
+                                     const std::string& baseUrl) :
     IObservable(broker),
     IObserver(broker),
-    orthanc_(orthanc)
+    web_(web),
+    baseUrl_(baseUrl)
   {
   }
 
@@ -182,12 +184,13 @@
     MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
     Orthanc::IDynamicObject* payload)
   {
-    orthanc_.GetAsync(uri, IWebService::Headers(),
-                      new WebServicePayload(successCallback, failureCallback, payload),
-                      new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                      (*this, &OrthancApiClient::NotifyHttpSuccess),
-                      new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                      (*this, &OrthancApiClient::NotifyHttpError));
+    web_.GetAsync(baseUrl_ + uri,
+                  IWebService::HttpHeaders(),
+                  new WebServicePayload(successCallback, failureCallback, payload),
+                  new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                  (*this, &OrthancApiClient::NotifyHttpSuccess),
+                  new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                  (*this, &OrthancApiClient::NotifyHttpError));
   }
 
 
@@ -198,7 +201,7 @@
     MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
     Orthanc::IDynamicObject* payload)
   {
-    IWebService::Headers headers;
+    IWebService::HttpHeaders headers;
     headers["Accept"] = contentType;
     GetBinaryAsync(uri, headers, successCallback, failureCallback, payload);
   }
@@ -206,17 +209,17 @@
 
   void OrthancApiClient::GetBinaryAsync(
     const std::string& uri,
-    const IWebService::Headers& headers,
+    const IWebService::HttpHeaders& headers,
     MessageHandler<BinaryResponseReadyMessage>* successCallback,
     MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
     Orthanc::IDynamicObject* payload)
   {
-    orthanc_.GetAsync(uri, headers,
-                      new WebServicePayload(successCallback, failureCallback, payload),
-                      new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                      (*this, &OrthancApiClient::NotifyHttpSuccess),
-                      new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                      (*this, &OrthancApiClient::NotifyHttpError));
+    web_.GetAsync(baseUrl_ + uri, headers,
+                  new WebServicePayload(successCallback, failureCallback, payload),
+                  new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                  (*this, &OrthancApiClient::NotifyHttpSuccess),
+                  new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                  (*this, &OrthancApiClient::NotifyHttpError));
   }
 
   
@@ -227,12 +230,12 @@
     MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
     Orthanc::IDynamicObject* payload)
   {
-    orthanc_.PostAsync(uri, IWebService::Headers(), body,
-                       new WebServicePayload(successCallback, failureCallback, payload),
-                       new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                       (*this, &OrthancApiClient::NotifyHttpSuccess),
-                       new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                       (*this, &OrthancApiClient::NotifyHttpError));
+    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body,
+                   new WebServicePayload(successCallback, failureCallback, payload),
+                   new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                   (*this, &OrthancApiClient::NotifyHttpSuccess),
+                   new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                   (*this, &OrthancApiClient::NotifyHttpError));
 
   }
 
@@ -256,12 +259,12 @@
     MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
     Orthanc::IDynamicObject* payload)
   {
-    orthanc_.DeleteAsync(uri, IWebService::Headers(),
-                         new WebServicePayload(successCallback, failureCallback, payload),
-                         new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                         (*this, &OrthancApiClient::NotifyHttpSuccess),
-                         new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                         (*this, &OrthancApiClient::NotifyHttpError));
+    web_.DeleteAsync(baseUrl_ + uri, IWebService::HttpHeaders(),
+                     new WebServicePayload(successCallback, failureCallback, payload),
+                     new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                     (*this, &OrthancApiClient::NotifyHttpSuccess),
+                     new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                     (*this, &OrthancApiClient::NotifyHttpError));
   }
 
 
--- a/Framework/Toolbox/OrthancApiClient.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Framework/Toolbox/OrthancApiClient.h	Thu Nov 15 17:28:15 2018 +0100
@@ -150,13 +150,14 @@
   private:
     class WebServicePayload;
     
-
   protected:
-    IWebService&                      orthanc_;
+    IWebService&  web_;
+    std::string   baseUrl_;
 
   public:
     OrthancApiClient(MessageBroker& broker,
-                     IWebService& orthanc);
+                     IWebService& web,
+                     const std::string& baseUrl);
     
     virtual ~OrthancApiClient()
     {
@@ -177,7 +178,7 @@
 
     // schedule a GET request expecting a binary response.
     void GetBinaryAsync(const std::string& uri,
-                        const IWebService::Headers& headers,
+                        const IWebService::HttpHeaders& headers,
                         MessageHandler<BinaryResponseReadyMessage>* successCallback,
                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
                         Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Framework/Toolbox/OrthancSlicesLoader.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -879,7 +879,7 @@
     {
       std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
                          boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz");
-      orthanc_.GetBinaryAsync(uri, IWebService::Headers(),
+      orthanc_.GetBinaryAsync(uri, IWebService::HttpHeaders(),
                               new Callable<OrthancSlicesLoader, OrthancApiClient::BinaryResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseSliceRawImage),
                               new Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnSliceImageError),
                               Operation::DownloadSliceRawImage(index, slice));
--- a/Platforms/Generic/OracleWebService.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/OracleWebService.h	Thu Nov 15 17:28:15 2018 +0100
@@ -53,7 +53,7 @@
     }
 
     virtual void GetAsync(const std::string& uri,
-                          const Headers& headers,
+                          const HttpHeaders& headers,
                           Orthanc::IDynamicObject* payload, // takes ownership
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
@@ -63,7 +63,7 @@
     }
 
     virtual void PostAsync(const std::string& uri,
-                           const Headers& headers,
+                           const HttpHeaders& headers,
                            const std::string& body,
                            Orthanc::IDynamicObject* payload, // takes ownership
                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
@@ -74,7 +74,7 @@
     }
 
     virtual void DeleteAsync(const std::string& uri,
-                             const Headers& headers,
+                             const HttpHeaders& headers,
                              Orthanc::IDynamicObject* payload,
                              MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
                              MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
--- a/Platforms/Generic/WebServiceCommandBase.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServiceCommandBase.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -29,8 +29,8 @@
                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
                                                MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
                                                const Orthanc::WebServiceParameters& parameters,
-                                               const std::string& uri,
-                                               const IWebService::Headers& headers,
+                                               const std::string& url,
+                                               const IWebService::HttpHeaders& headers,
                                                unsigned int timeoutInSeconds,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
                                                NativeStoneApplicationContext& context) :
@@ -38,7 +38,7 @@
     successCallback_(successCallback),
     failureCallback_(failureCallback),
     parameters_(parameters),
-    uri_(uri),
+    url_(url),
     headers_(headers),
     payload_(payload),
     context_(context),
@@ -56,12 +56,13 @@
 
     if (success_ && successCallback_.get() != NULL)
     {
-      IWebService::HttpRequestSuccessMessage message(uri_, answer_.c_str(), answer_.size(), payload_.get());
+      IWebService::HttpRequestSuccessMessage message
+        (url_, answer_.c_str(), answer_.size(), answerHeaders_, payload_.get());
       successCallback_->Apply(message);
     }
     else if (!success_ && failureCallback_.get() != NULL)
     {
-      IWebService::HttpRequestErrorMessage message(uri_, payload_.get());
+      IWebService::HttpRequestErrorMessage message(url_, payload_.get());
       failureCallback_->Apply(message);
     }
   }
--- a/Platforms/Generic/WebServiceCommandBase.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServiceCommandBase.h	Thu Nov 15 17:28:15 2018 +0100
@@ -40,11 +40,12 @@
     std::auto_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
     std::auto_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
     Orthanc::WebServiceParameters           parameters_;
-    std::string                             uri_;
-    std::map<std::string, std::string>      headers_;
+    std::string                             url_;
+    IWebService::HttpHeaders                headers_;
     std::auto_ptr<Orthanc::IDynamicObject>  payload_;
     bool                                    success_;
     std::string                             answer_;
+    IWebService::HttpHeaders                answerHeaders_;
     NativeStoneApplicationContext&          context_;
     unsigned int                            timeoutInSeconds_;
 
@@ -53,8 +54,8 @@
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                           const Orthanc::WebServiceParameters& parameters,
-                          const std::string& uri,
-                          const std::map<std::string, std::string>& headers,
+                          const std::string& url,
+                          const IWebService::HttpHeaders& headers,
                           unsigned int timeoutInSeconds,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
                           NativeStoneApplicationContext& context
--- a/Platforms/Generic/WebServiceDeleteCommand.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServiceDeleteCommand.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -29,27 +29,28 @@
                                                    MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                                                    MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                                                    const Orthanc::WebServiceParameters& parameters,
-                                                   const std::string& uri,
-                                                   const IWebService::Headers& headers,
+                                                   const std::string& url,
+                                                   const IWebService::HttpHeaders& headers,
                                                    unsigned int timeoutInSeconds,
                                                    Orthanc::IDynamicObject* payload /* takes ownership */,
                                                    NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, timeoutInSeconds, payload, context)
+    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
   {
   }
 
   void WebServiceDeleteCommand::Execute()
   {
-    Orthanc::HttpClient client(parameters_, uri_);
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
     client.SetTimeout(timeoutInSeconds_);
     client.SetMethod(Orthanc::HttpMethod_Delete);
 
-    for (IWebService::Headers::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
     {
       client.AddHeader(it->first, it->second);
     }
 
-    success_ = client.Apply(answer_);
+    success_ = client.Apply(answer_, answerHeaders_);
   }
 
 }
--- a/Platforms/Generic/WebServiceDeleteCommand.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServiceDeleteCommand.h	Thu Nov 15 17:28:15 2018 +0100
@@ -32,8 +32,8 @@
                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                             const Orthanc::WebServiceParameters& parameters,
-                            const std::string& uri,
-                            const IWebService::Headers& headers,
+                            const std::string& url,
+                            const IWebService::HttpHeaders& headers,
                             unsigned int timeoutInSeconds,
                             Orthanc::IDynamicObject* payload /* takes ownership */,
                             NativeStoneApplicationContext& context);
--- a/Platforms/Generic/WebServiceGetCommand.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServiceGetCommand.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -30,28 +30,29 @@
                                              MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                                              MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                                              const Orthanc::WebServiceParameters& parameters,
-                                             const std::string& uri,
-                                             const IWebService::Headers& headers,
+                                             const std::string& url,
+                                             const IWebService::HttpHeaders& headers,
                                              unsigned int timeoutInSeconds,
                                              Orthanc::IDynamicObject* payload /* takes ownership */,
                                              NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, timeoutInSeconds, payload, context)
+    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
   {
   }
 
 
   void WebServiceGetCommand::Execute()
   {
-    Orthanc::HttpClient client(parameters_, uri_);
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
     client.SetTimeout(timeoutInSeconds_);
     client.SetMethod(Orthanc::HttpMethod_Get);
 
-    for (IWebService::Headers::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
     {
       client.AddHeader(it->first, it->second);
     }
 
-    success_ = client.Apply(answer_);
+    success_ = client.Apply(answer_, answerHeaders_);
   }
 
 }
--- a/Platforms/Generic/WebServiceGetCommand.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServiceGetCommand.h	Thu Nov 15 17:28:15 2018 +0100
@@ -32,8 +32,8 @@
                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                          const Orthanc::WebServiceParameters& parameters,
-                         const std::string& uri,
-                         const IWebService::Headers& headers,
+                         const std::string& url,
+                         const IWebService::HttpHeaders& headers,
                          unsigned int timeoutInSeconds,
                          Orthanc::IDynamicObject* payload /* takes ownership */,
                          NativeStoneApplicationContext& context);
--- a/Platforms/Generic/WebServicePostCommand.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServicePostCommand.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -29,30 +29,30 @@
                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                                                MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                                                const Orthanc::WebServiceParameters& parameters,
-                                               const std::string& uri,
-                                               const IWebService::Headers& headers,
+                                               const std::string& url,
+                                               const IWebService::HttpHeaders& headers,
                                                unsigned int timeoutInSeconds,
                                                const std::string& body,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
                                                NativeStoneApplicationContext& context) :
-    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, uri, headers, timeoutInSeconds, payload, context),
+    WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context),
     body_(body)
   {
   }
 
   void WebServicePostCommand::Execute()
   {
-    Orthanc::HttpClient client(parameters_, uri_);
+    Orthanc::HttpClient client(parameters_, "/");
+    client.SetUrl(url_);
     client.SetTimeout(timeoutInSeconds_);
     client.SetMethod(Orthanc::HttpMethod_Post);
     client.GetBody().swap(body_);
 
-    for (IWebService::Headers::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
     {
       client.AddHeader(it->first, it->second);
     }
 
-    success_ = client.Apply(answer_);
+    success_ = client.Apply(answer_, answerHeaders_);
   }
-
 }
--- a/Platforms/Generic/WebServicePostCommand.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Generic/WebServicePostCommand.h	Thu Nov 15 17:28:15 2018 +0100
@@ -28,15 +28,15 @@
   class WebServicePostCommand : public WebServiceCommandBase
   {
   protected:
-    std::string                             body_;
+    std::string  body_;
 
   public:
     WebServicePostCommand(MessageBroker& broker,
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                           const Orthanc::WebServiceParameters& parameters,
-                          const std::string& uri,
-                          const IWebService::Headers& headers,
+                          const std::string& url,
+                          const IWebService::HttpHeaders& headers,
                           unsigned int timeoutInSeconds,
                           const std::string& body,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
--- a/Platforms/Wasm/Defaults.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Wasm/Defaults.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -93,7 +93,7 @@
     startupParametersBuilder.GetStartupParameters(parameters, options);
 
     context.reset(new OrthancStone::StoneApplicationContext());
-    context->SetWebService(OrthancStone::WasmWebService::GetInstance());
+    context->Initialize(broker, OrthancStone::WasmWebService::GetInstance(), "");
     application->Initialize(context.get(), statusBar_, parameters);
     application->InitializeWasm();
 
--- a/Platforms/Wasm/WasmWebService.cpp	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Wasm/WasmWebService.cpp	Thu Nov 15 17:28:15 2018 +0100
@@ -49,6 +49,7 @@
                                                          const char* uri,
                                                          const void* body,
                                                          size_t bodySize,
+                                                         const char* answerHeaders,
                                                          void* payload)
   {
     if (successCallable == NULL)
@@ -57,9 +58,15 @@
     }
     else
     {
+      OrthancStone::IWebService::HttpHeaders headers;
+
+      // TODO - Parse "answerHeaders"
+      printf("[%s]\n", answerHeaders);
+      
       reinterpret_cast<OrthancStone::MessageHandler<OrthancStone::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
-        Apply(OrthancStone::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
-   }
+        Apply(OrthancStone::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
+                                                                   reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+    }
   }
 
   void EMSCRIPTEN_KEEPALIVE WasmWebService_SetBaseUri(const char* baseUri)
@@ -91,10 +98,10 @@
     }
   }
 
-  void ToJsonString(std::string& output, const IWebService::Headers& headers)
+  void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers)
   {
     Json::Value jsonHeaders;
-    for (IWebService::Headers::const_iterator it = headers.begin(); it != headers.end(); it++ )
+    for (IWebService::HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
     {
       jsonHeaders[it->first] = it->second;
     }
@@ -108,7 +115,7 @@
   }
 
   void WasmWebService::PostAsync(const std::string& relativeUri,
-                                 const Headers& headers,
+                                 const HttpHeaders& headers,
                                  const std::string& body,
                                  Orthanc::IDynamicObject* payload,
                                  MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
@@ -119,11 +126,11 @@
     std::string headersInJsonString;
     ToJsonString(headersInJsonString, headers);
     WasmWebService_PostAsync(successCallable, failureCallable, uri.c_str(), headersInJsonString.c_str(),
-                                       body.c_str(), body.size(), payload, timeoutInSeconds);
+                             body.c_str(), body.size(), payload, timeoutInSeconds);
   }
 
   void WasmWebService::DeleteAsync(const std::string& relativeUri,
-                                   const Headers& headers,
+                                   const HttpHeaders& headers,
                                    Orthanc::IDynamicObject* payload,
                                    MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
                                    MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
@@ -137,16 +144,15 @@
   }
 
   void WasmWebService::GetAsync(const std::string& relativeUri,
-                                 const Headers& headers,
-                                 Orthanc::IDynamicObject* payload,
-                                 MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                 MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
-                                 unsigned int timeoutInSeconds)
+                                const HttpHeaders& headers,
+                                Orthanc::IDynamicObject* payload,
+                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                unsigned int timeoutInSeconds)
   {
     std::string uri = baseUri_ + relativeUri;
     std::string headersInJsonString;
     ToJsonString(headersInJsonString, headers);
     WasmWebService_GetAsync(successCallable, failureCallable, uri.c_str(), headersInJsonString.c_str(), payload, timeoutInSeconds);
   }
-
 }
--- a/Platforms/Wasm/WasmWebService.h	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Wasm/WasmWebService.h	Thu Nov 15 17:28:15 2018 +0100
@@ -38,14 +38,14 @@
     void SetBaseUri(const std::string baseUri);
 
     virtual void GetAsync(const std::string& uri,
-                          const Headers& headers,
+                          const HttpHeaders& headers,
                           Orthanc::IDynamicObject* payload,
                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable = NULL,
                           unsigned int timeoutInSeconds = 60);
 
     virtual void PostAsync(const std::string& uri,
-                           const Headers& headers,
+                           const HttpHeaders& headers,
                            const std::string& body,
                            Orthanc::IDynamicObject* payload,
                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
@@ -53,7 +53,7 @@
                            unsigned int timeoutInSeconds = 60);
 
     virtual void DeleteAsync(const std::string& uri,
-                             const Headers& headers,
+                             const HttpHeaders& headers,
                              Orthanc::IDynamicObject* payload,
                              MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
                              MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable = NULL,
--- a/Platforms/Wasm/WasmWebService.js	Tue Nov 13 10:36:53 2018 +0100
+++ b/Platforms/Wasm/WasmWebService.js	Thu Nov 15 17:28:15 2018 +0100
@@ -17,11 +17,15 @@
     xhr.onreadystatechange = function() {
       if (this.readyState == XMLHttpRequest.DONE) {
         if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          writeStringToMemory(s, headers);
+          
           // TODO - Is "new Uint8Array()" necessary? This copies the
           // answer to the WebAssembly stack, hence necessitating
           // increasing the TOTAL_STACK parameter of Emscripten
           WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, payload);
+                                       this.response.byteLength, headers, payload);
         } else {
           WasmWebService_NotifyError(callableFailure, url_, payload);
         }
@@ -48,8 +52,12 @@
     xhr.onreadystatechange = function() {
       if (this.readyState == XMLHttpRequest.DONE) {
         if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          writeStringToMemory(s, headers);
+
           WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, payload);
+                                       this.response.byteLength, headers, payload);
         } else {
           WasmWebService_NotifyError(callableFailure, url_, payload);
         }
@@ -76,8 +84,12 @@
     xhr.onreadystatechange = function() {
       if (this.readyState == XMLHttpRequest.DONE) {
         if (xhr.status === 200) {
+          var s = xhr.getAllResponseHeaders();
+          var headers = _malloc(s.length + 1);
+          writeStringToMemory(s, headers);
+
           WasmWebService_NotifySuccess(callableSuccess, url_, new Uint8Array(this.response),
-                                       this.response.byteLength, payload);
+                                       this.response.byteLength, headers, payload);
         } else {
           WasmWebService_NotifyError(callableFailure, url_, payload);
         }