changeset 2040:6ea2e264ca50

retrieval of HTTP headers in answers within HttpClient
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 21 Jun 2016 16:34:30 +0200
parents e33e0ae51d7b
children 9f61ca1e3eb3
files Core/ChunkedBuffer.cpp Core/ChunkedBuffer.h Core/HttpClient.cpp Core/HttpClient.h
diffstat 4 files changed, 101 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/Core/ChunkedBuffer.cpp	Tue Jun 21 15:17:49 2016 +0200
+++ b/Core/ChunkedBuffer.cpp	Tue Jun 21 16:34:30 2016 +0200
@@ -51,17 +51,19 @@
   }
 
 
-  void ChunkedBuffer::AddChunk(const char* chunkData,
+  void ChunkedBuffer::AddChunk(const void* chunkData,
                                size_t chunkSize)
   {
     if (chunkSize == 0)
     {
       return;
     }
-
-    assert(chunkData != NULL);
-    chunks_.push_back(new std::string(chunkData, chunkSize));
-    numBytes_ += chunkSize;
+    else
+    {
+      assert(chunkData != NULL);
+      chunks_.push_back(new std::string(reinterpret_cast<const char*>(chunkData), chunkSize));
+      numBytes_ += chunkSize;
+    }
   }
 
 
--- a/Core/ChunkedBuffer.h	Tue Jun 21 15:17:49 2016 +0200
+++ b/Core/ChunkedBuffer.h	Tue Jun 21 16:34:30 2016 +0200
@@ -61,7 +61,7 @@
       return numBytes_;
     }
 
-    void AddChunk(const char* chunkData,
+    void AddChunk(const void* chunkData,
                   size_t chunkSize);
 
     void AddChunk(const std::string& chunk);
--- a/Core/HttpClient.cpp	Tue Jun 21 15:17:49 2016 +0200
+++ b/Core/HttpClient.cpp	Tue Jun 21 16:34:30 2016 +0200
@@ -36,6 +36,7 @@
 #include "Toolbox.h"
 #include "OrthancException.h"
 #include "Logging.h"
+#include "ChunkedBuffer.h"
 
 #include <string.h>
 #include <curl/curl.h>
@@ -218,18 +219,51 @@
 
   static size_t CurlBodyCallback(void *buffer, size_t size, size_t nmemb, void *payload)
   {
-    std::string& target = *(static_cast<std::string*>(payload));
+    ChunkedBuffer& target = *(static_cast<ChunkedBuffer*>(payload));
+
+    size_t length = size * nmemb;
+    if (length == 0)
+    {
+      return 0;
+    }
+    else
+    {
+      target.AddChunk(buffer, length);
+      return length;
+    }
+  }
+
+
+  static size_t CurlHeaderCallback(void *buffer, size_t size, size_t nmemb, void *payload)
+  {
+    HttpClient::HttpHeaders& headers = *(static_cast<HttpClient::HttpHeaders*>(payload));
 
     size_t length = size * nmemb;
     if (length == 0)
+    {
       return 0;
-
-    size_t pos = target.size();
+    }
+    else
+    {
+      std::string s(reinterpret_cast<const char*>(buffer), length);
+      std::size_t colon = s.find(':');
+      std::size_t eol = s.find("\r\n");
+      if (colon != std::string::npos &&
+          eol != std::string::npos)
+      {
+        std::string tmp;
+        Toolbox::ToLowerCase(tmp, s.substr(0, colon));
+        std::string key = Toolbox::StripSpaces(tmp);
 
-    target.resize(pos + length);
-    memcpy(&target.at(pos), buffer, length);
+        if (!key.empty())
+        {
+          std::string value = Toolbox::StripSpaces(s.substr(colon + 1, eol));
+          headers[key] = value;
+        }
+      }
 
-    return length;
+      return length;
+    }
   }
 
 
@@ -354,11 +388,22 @@
   }
 
 
-  bool HttpClient::ApplyInternal(std::string& answer)
+  bool HttpClient::ApplyInternal(std::string& answer,
+                                 HttpHeaders* headers)
   {
     answer.clear();
     CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str()));
-    CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer));
+
+    if (headers == NULL)
+    {
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, NULL));
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, NULL));
+    }
+    else
+    {
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, &CurlHeaderCallback));
+      CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, headers));
+    }
 
 #if ORTHANC_SSL_ENABLED == 1
     // Setup HTTPS-related options
@@ -520,6 +565,9 @@
     CURLcode code;
     long status = 0;
 
+    ChunkedBuffer buffer;
+    CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &buffer));
+
     if (boost::starts_with(url_, "https://"))
     {
       code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status);
@@ -543,8 +591,13 @@
 
     bool success = (status >= 200 && status < 300);
 
-    if (!success)
+    if (success)
     {
+      buffer.Flatten(answer);
+    }
+    else
+    {
+      answer.clear();
       LOG(INFO) << "Error in HTTP request, received HTTP status " << status 
                 << " (" << EnumerationToString(lastStatus_) << ")";
     }
@@ -553,16 +606,11 @@
   }
 
 
-  bool HttpClient::Apply(std::string& answer)
-  {
-    return ApplyInternal(answer);
-  }
-
-
-  bool HttpClient::Apply(Json::Value& answer)
+  bool HttpClient::ApplyInternal(Json::Value& answer,
+                                 HttpClient::HttpHeaders* answerHeaders)
   {
     std::string s;
-    if (Apply(s))
+    if (ApplyInternal(s, answerHeaders))
     {
       Json::Reader reader;
       return reader.parse(s, answer);
--- a/Core/HttpClient.h	Tue Jun 21 15:17:49 2016 +0200
+++ b/Core/HttpClient.h	Tue Jun 21 16:34:30 2016 +0200
@@ -43,6 +43,9 @@
 {
   class HttpClient
   {
+  public:
+    typedef std::map<std::string, std::string>  HttpHeaders;
+
   private:
     class GlobalParameters;
 
@@ -69,7 +72,11 @@
     void operator= (const HttpClient&);  // Assignment forbidden
     HttpClient(const HttpClient& base);  // Copy forbidden
 
-    bool ApplyInternal(std::string& answer);
+    bool ApplyInternal(std::string& answer,
+                       HttpHeaders* answerHeaders);
+
+    bool ApplyInternal(Json::Value& answer,
+                       HttpHeaders* answerHeaders);
 
   public:
     HttpClient();
@@ -141,9 +148,27 @@
 
     void ClearHeaders();
 
-    bool Apply(std::string& answer);
+    bool Apply(std::string& answer)
+    {
+      return ApplyInternal(answer, NULL);
+    }
+
+    bool Apply(Json::Value& answer)
+    {
+      return ApplyInternal(answer, NULL);
+    }
 
-    bool Apply(Json::Value& answer);
+    bool Apply(std::string& answer,
+               HttpHeaders& answerHeaders)
+    {
+      return ApplyInternal(answer, &answerHeaders);
+    }
+
+    bool Apply(Json::Value& answer,
+               HttpHeaders& answerHeaders)
+    {
+      return ApplyInternal(answer, &answerHeaders);
+    }
 
     HttpStatus GetLastStatus() const
     {