1427 lines
36 KiB
C++
1427 lines
36 KiB
C++
/**
|
|
* Orthanc - A Lightweight, RESTful DICOM Store
|
|
* Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
|
|
* Department, University Hospital of Liege, Belgium
|
|
* Copyright (C) 2017-2023 Osimis S.A., Belgium
|
|
* Copyright (C) 2024-2025 Orthanc Team SRL, Belgium
|
|
* Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
|
|
*
|
|
* This program is free software: you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
|
|
#include "PrecompiledHeaders.h"
|
|
#include "HttpClient.h"
|
|
|
|
#include "Toolbox.h"
|
|
#include "OrthancException.h"
|
|
#include "Logging.h"
|
|
#include "ChunkedBuffer.h"
|
|
#include "SystemToolbox.h"
|
|
|
|
#include <string.h>
|
|
#include <curl/curl.h>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/thread/mutex.hpp>
|
|
|
|
// Default timeout = 60 seconds (in Orthanc <= 1.5.6, it was 10 seconds)
|
|
static const unsigned int DEFAULT_HTTP_TIMEOUT = 60;
|
|
|
|
|
|
#if ORTHANC_ENABLE_PKCS11 == 1
|
|
# include "Pkcs11.h"
|
|
#endif
|
|
|
|
|
|
extern "C"
|
|
{
|
|
static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status, const std::string& url)
|
|
{
|
|
if (code == CURLE_OK)
|
|
{
|
|
code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status);
|
|
return code;
|
|
}
|
|
else
|
|
{
|
|
*status = 0;
|
|
return code;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is a dummy wrapper function to suppress any OpenSSL-related
|
|
// problem in valgrind. Inlining is prevented.
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
__attribute__((noinline))
|
|
#endif
|
|
static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status, const std::string& url)
|
|
{
|
|
#if ORTHANC_ENABLE_SSL == 1
|
|
return GetHttpStatus(curl_easy_perform(curl), curl, status, url);
|
|
#else
|
|
throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
|
|
"Orthanc was compiled without SSL support, "
|
|
"cannot make HTTPS request");
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
namespace Orthanc
|
|
{
|
|
static CURLcode CheckCode(CURLcode code)
|
|
{
|
|
if (code == CURLE_NOT_BUILT_IN)
|
|
{
|
|
throw OrthancException(ErrorCode_InternalError,
|
|
"Your libcurl does not contain a required feature, "
|
|
"please recompile Orthanc with -DUSE_SYSTEM_CURL=OFF");
|
|
}
|
|
|
|
if (code != CURLE_OK)
|
|
{
|
|
throw OrthancException(ErrorCode_NetworkProtocol,
|
|
"libCURL error: " + std::string(curl_easy_strerror(code)));
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
static CURLcode CheckCode(CURLcode code, const std::string& url)
|
|
{
|
|
if (code == CURLE_NOT_BUILT_IN)
|
|
{
|
|
throw OrthancException(ErrorCode_InternalError,
|
|
"Your libcurl does not contain a required feature, "
|
|
"please recompile Orthanc with -DUSE_SYSTEM_CURL=OFF");
|
|
}
|
|
|
|
if (code != CURLE_OK)
|
|
{
|
|
throw OrthancException(ErrorCode_NetworkProtocol,
|
|
"libCURL error: " + std::string(curl_easy_strerror(code)) + " while accessing " + url);
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
|
|
// RAII pattern around a "curl_slist"
|
|
class HttpClient::CurlHeaders : public boost::noncopyable
|
|
{
|
|
private:
|
|
struct curl_slist *content_;
|
|
bool isChunkedTransfer_;
|
|
bool hasExpect_;
|
|
|
|
public:
|
|
CurlHeaders() :
|
|
content_(NULL),
|
|
isChunkedTransfer_(false),
|
|
hasExpect_(false)
|
|
{
|
|
}
|
|
|
|
explicit CurlHeaders(const HttpClient::HttpHeaders& headers)
|
|
{
|
|
for (HttpClient::HttpHeaders::const_iterator
|
|
it = headers.begin(); it != headers.end(); ++it)
|
|
{
|
|
AddHeader(it->first, it->second);
|
|
}
|
|
}
|
|
|
|
~CurlHeaders()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
bool IsEmpty() const
|
|
{
|
|
return content_ == NULL;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
if (content_ != NULL)
|
|
{
|
|
curl_slist_free_all(content_);
|
|
content_ = NULL;
|
|
}
|
|
|
|
isChunkedTransfer_ = false;
|
|
hasExpect_ = false;
|
|
}
|
|
|
|
void AddHeader(const std::string& key,
|
|
const std::string& value)
|
|
{
|
|
if (boost::iequals(key, "Expect"))
|
|
{
|
|
hasExpect_ = true;
|
|
}
|
|
|
|
if (boost::iequals(key, "Transfer-Encoding") &&
|
|
value == "chunked")
|
|
{
|
|
isChunkedTransfer_ = true;
|
|
}
|
|
|
|
std::string item = key + ": " + value;
|
|
|
|
struct curl_slist *tmp = curl_slist_append(content_, item.c_str());
|
|
|
|
if (tmp == NULL)
|
|
{
|
|
throw OrthancException(ErrorCode_NotEnoughMemory);
|
|
}
|
|
else
|
|
{
|
|
content_ = tmp;
|
|
}
|
|
}
|
|
|
|
void Assign(CURL* curl) const
|
|
{
|
|
CheckCode(curl_easy_setopt(curl, CURLOPT_HTTPHEADER, content_));
|
|
}
|
|
|
|
bool HasExpect() const
|
|
{
|
|
return hasExpect_;
|
|
}
|
|
|
|
bool IsChunkedTransfer() const
|
|
{
|
|
return isChunkedTransfer_;
|
|
}
|
|
};
|
|
|
|
|
|
class HttpClient::CurlRequestBody : public boost::noncopyable
|
|
{
|
|
private:
|
|
HttpClient::IRequestBody* body_;
|
|
std::string pending_;
|
|
size_t pendingPos_;
|
|
|
|
size_t CallbackInternal(char* curlBuffer,
|
|
size_t curlBufferSize)
|
|
{
|
|
if (body_ == NULL)
|
|
{
|
|
throw OrthancException(ErrorCode_BadSequenceOfCalls);
|
|
}
|
|
|
|
if (curlBufferSize == 0)
|
|
{
|
|
throw OrthancException(ErrorCode_InternalError);
|
|
}
|
|
|
|
if (pendingPos_ + curlBufferSize <= pending_.size())
|
|
{
|
|
assert(sizeof(char) == 1);
|
|
memcpy(curlBuffer, &pending_[pendingPos_], curlBufferSize);
|
|
pendingPos_ += curlBufferSize;
|
|
return curlBufferSize;
|
|
}
|
|
else
|
|
{
|
|
ChunkedBuffer buffer;
|
|
buffer.SetPendingBufferSize(curlBufferSize);
|
|
|
|
if (pendingPos_ < pending_.size())
|
|
{
|
|
buffer.AddChunk(&pending_[pendingPos_], pending_.size() - pendingPos_);
|
|
}
|
|
|
|
// Read chunks from the body stream so as to fill the target buffer
|
|
std::string chunk;
|
|
|
|
while (buffer.GetNumBytes() < curlBufferSize &&
|
|
body_->ReadNextChunk(chunk))
|
|
{
|
|
buffer.AddChunk(chunk);
|
|
}
|
|
|
|
buffer.Flatten(pending_);
|
|
pendingPos_ = std::min(pending_.size(), curlBufferSize);
|
|
|
|
if (pendingPos_ != 0)
|
|
{
|
|
memcpy(curlBuffer, pending_.c_str(), pendingPos_);
|
|
}
|
|
|
|
return pendingPos_;
|
|
}
|
|
}
|
|
|
|
public:
|
|
CurlRequestBody() :
|
|
body_(NULL),
|
|
pendingPos_(0)
|
|
{
|
|
}
|
|
|
|
void SetBody(HttpClient::IRequestBody& body)
|
|
{
|
|
body_ = &body;
|
|
pending_.clear();
|
|
pendingPos_ = 0;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
body_ = NULL;
|
|
pending_.clear();
|
|
pendingPos_ = 0;
|
|
}
|
|
|
|
bool IsValid() const
|
|
{
|
|
return body_ != NULL;
|
|
}
|
|
|
|
static size_t Callback(char *buffer, size_t size, size_t nitems, void *userdata)
|
|
{
|
|
try
|
|
{
|
|
assert(userdata != NULL);
|
|
return reinterpret_cast<HttpClient::CurlRequestBody*>(userdata)->
|
|
CallbackInternal(buffer, size * nitems);
|
|
}
|
|
catch (OrthancException& e)
|
|
{
|
|
LOG(ERROR) << "Exception while streaming HTTP body: " << e.What();
|
|
return CURL_READFUNC_ABORT;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG(ERROR) << "Native exception while streaming HTTP body";
|
|
return CURL_READFUNC_ABORT;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class HttpClient::CurlAnswer : public boost::noncopyable
|
|
{
|
|
private:
|
|
HttpClient::IAnswer& answer_;
|
|
bool headersLowerCase_;
|
|
|
|
public:
|
|
CurlAnswer(HttpClient::IAnswer& answer,
|
|
bool headersLowerCase) :
|
|
answer_(answer),
|
|
headersLowerCase_(headersLowerCase)
|
|
{
|
|
}
|
|
|
|
static size_t HeaderCallback(void *buffer, size_t size, size_t nmemb, void *userdata)
|
|
{
|
|
try
|
|
{
|
|
assert(userdata != NULL);
|
|
|
|
size_t length = size * nmemb;
|
|
if (length == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
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)
|
|
{
|
|
CurlAnswer& that = *(static_cast<CurlAnswer*>(userdata));
|
|
std::string tmp(s.substr(0, colon));
|
|
|
|
if (that.headersLowerCase_)
|
|
{
|
|
Toolbox::ToLowerCase(tmp);
|
|
}
|
|
|
|
std::string key = Toolbox::StripSpaces(tmp);
|
|
|
|
if (!key.empty())
|
|
{
|
|
std::string value = Toolbox::StripSpaces(s.substr(colon + 1, eol));
|
|
|
|
that.answer_.AddHeader(key, value);
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
}
|
|
catch (OrthancException& e)
|
|
{
|
|
LOG(ERROR) << "Exception while streaming HTTP body: " << e.What();
|
|
return CURL_READFUNC_ABORT;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG(ERROR) << "Native exception while streaming HTTP body";
|
|
return CURL_READFUNC_ABORT;
|
|
}
|
|
}
|
|
|
|
static size_t BodyCallback(void *buffer, size_t size, size_t nmemb, void *userdata)
|
|
{
|
|
try
|
|
{
|
|
assert(userdata != NULL);
|
|
|
|
size_t length = size * nmemb;
|
|
if (length == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
CurlAnswer& that = *(static_cast<CurlAnswer*>(userdata));
|
|
that.answer_.AddChunk(buffer, length);
|
|
return length;
|
|
}
|
|
}
|
|
catch (OrthancException& e)
|
|
{
|
|
LOG(ERROR) << "Exception while streaming HTTP body: " << e.What();
|
|
return CURL_READFUNC_ABORT;
|
|
}
|
|
catch (...)
|
|
{
|
|
LOG(ERROR) << "Native exception while streaming HTTP body";
|
|
return CURL_READFUNC_ABORT;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
class HttpClient::DefaultAnswer : public HttpClient::IAnswer
|
|
{
|
|
private:
|
|
ChunkedBuffer answer_;
|
|
HttpHeaders* headers_;
|
|
|
|
public:
|
|
DefaultAnswer() : headers_(NULL)
|
|
{
|
|
}
|
|
|
|
void SetHeaders(HttpHeaders& headers)
|
|
{
|
|
headers_ = &headers;
|
|
headers_->clear();
|
|
}
|
|
|
|
void FlattenBody(std::string& target)
|
|
{
|
|
answer_.Flatten(target);
|
|
}
|
|
|
|
virtual void AddHeader(const std::string& key,
|
|
const std::string& value) ORTHANC_OVERRIDE
|
|
{
|
|
if (headers_ != NULL)
|
|
{
|
|
(*headers_) [key] = value;
|
|
}
|
|
}
|
|
|
|
virtual void AddChunk(const void* data,
|
|
size_t size) ORTHANC_OVERRIDE
|
|
{
|
|
answer_.AddChunk(data, size);
|
|
}
|
|
};
|
|
|
|
|
|
class HttpClient::GlobalParameters
|
|
{
|
|
private:
|
|
boost::mutex mutex_;
|
|
bool httpsVerifyPeers_;
|
|
std::string httpsCACertificates_;
|
|
std::string proxy_;
|
|
long timeout_;
|
|
bool verbose_;
|
|
|
|
GlobalParameters() :
|
|
httpsVerifyPeers_(true),
|
|
timeout_(0),
|
|
verbose_(false)
|
|
{
|
|
}
|
|
|
|
public:
|
|
// Singleton pattern
|
|
static GlobalParameters& GetInstance()
|
|
{
|
|
static GlobalParameters parameters;
|
|
return parameters;
|
|
}
|
|
|
|
void ConfigureSsl(bool httpsVerifyPeers,
|
|
const std::string& httpsCACertificates)
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
httpsVerifyPeers_ = httpsVerifyPeers;
|
|
httpsCACertificates_ = httpsCACertificates;
|
|
}
|
|
|
|
void GetSslConfiguration(bool& httpsVerifyPeers,
|
|
std::string& httpsCACertificates)
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
httpsVerifyPeers = httpsVerifyPeers_;
|
|
httpsCACertificates = httpsCACertificates_;
|
|
}
|
|
|
|
void SetDefaultProxy(const std::string& proxy)
|
|
{
|
|
CLOG(INFO, HTTP) << "Setting the default proxy for HTTP client connections: " << proxy;
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
proxy_ = proxy;
|
|
}
|
|
}
|
|
|
|
void GetDefaultProxy(std::string& target)
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
target = proxy_;
|
|
}
|
|
|
|
void SetDefaultTimeout(long seconds)
|
|
{
|
|
CLOG(INFO, HTTP) << "Setting the default timeout for HTTP client connections: " << seconds << " seconds";
|
|
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
timeout_ = seconds;
|
|
}
|
|
}
|
|
|
|
long GetDefaultTimeout()
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
return timeout_;
|
|
}
|
|
|
|
#if ORTHANC_ENABLE_PKCS11 == 1
|
|
bool IsPkcs11Initialized()
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
return Pkcs11::IsInitialized();
|
|
}
|
|
|
|
void InitializePkcs11(const std::string& module,
|
|
const std::string& pin,
|
|
bool verbose)
|
|
{
|
|
boost::mutex::scoped_lock lock(mutex_);
|
|
Pkcs11::Initialize(module, pin, verbose);
|
|
}
|
|
#endif
|
|
|
|
bool IsDefaultVerbose() const
|
|
{
|
|
return verbose_;
|
|
}
|
|
|
|
void SetDefaultVerbose(bool verbose)
|
|
{
|
|
verbose_ = verbose;
|
|
}
|
|
};
|
|
|
|
|
|
struct HttpClient::PImpl
|
|
{
|
|
CURL* curl_;
|
|
CurlHeaders defaultPostHeaders_;
|
|
CurlHeaders defaultChunkedHeaders_;
|
|
CurlHeaders userHeaders_;
|
|
CurlRequestBody requestBody_;
|
|
};
|
|
|
|
|
|
void HttpClient::ThrowException(HttpStatus status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case HttpStatus_400_BadRequest:
|
|
throw OrthancException(ErrorCode_BadRequest);
|
|
|
|
case HttpStatus_401_Unauthorized:
|
|
case HttpStatus_403_Forbidden:
|
|
throw OrthancException(ErrorCode_Unauthorized);
|
|
|
|
case HttpStatus_404_NotFound:
|
|
throw OrthancException(ErrorCode_UnknownResource);
|
|
|
|
default:
|
|
throw OrthancException(ErrorCode_NetworkProtocol);
|
|
}
|
|
}
|
|
|
|
|
|
/*static int CurlDebugCallback(CURL *handle,
|
|
curl_infotype type,
|
|
char *data,
|
|
size_t size,
|
|
void *userptr)
|
|
{
|
|
switch (type)
|
|
{
|
|
case CURLINFO_TEXT:
|
|
case CURLINFO_HEADER_IN:
|
|
case CURLINFO_HEADER_OUT:
|
|
case CURLINFO_SSL_DATA_IN:
|
|
case CURLINFO_SSL_DATA_OUT:
|
|
case CURLINFO_END:
|
|
case CURLINFO_DATA_IN:
|
|
case CURLINFO_DATA_OUT:
|
|
{
|
|
std::string s(data, size);
|
|
CLOG(INFO, INFO) << "libcurl: " << s;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}*/
|
|
|
|
|
|
void HttpClient::Setup()
|
|
{
|
|
pimpl_->defaultPostHeaders_.AddHeader("Expect", "");
|
|
pimpl_->defaultChunkedHeaders_.AddHeader("Expect", "");
|
|
pimpl_->defaultChunkedHeaders_.AddHeader("Transfer-Encoding", "chunked");
|
|
|
|
pimpl_->curl_ = curl_easy_init();
|
|
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, &CurlAnswer::HeaderCallback));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlAnswer::BodyCallback));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1));
|
|
|
|
// This fixes the "longjmp causes uninitialized stack frame" crash
|
|
// that happens on modern Linux versions.
|
|
// http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOSIGNAL, 1));
|
|
|
|
url_ = "";
|
|
method_ = HttpMethod_Get;
|
|
lastStatus_ = HttpStatus_None;
|
|
SetVerbose(GlobalParameters::GetInstance().IsDefaultVerbose());
|
|
timeout_ = GlobalParameters::GetInstance().GetDefaultTimeout();
|
|
GlobalParameters::GetInstance().GetDefaultProxy(proxy_);
|
|
GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_);
|
|
|
|
hasExternalBody_ = false;
|
|
externalBodyData_ = NULL;
|
|
externalBodySize_ = 0;
|
|
}
|
|
|
|
|
|
HttpClient::HttpClient() :
|
|
pimpl_(new PImpl),
|
|
verifyPeers_(true),
|
|
pkcs11Enabled_(false),
|
|
headersToLowerCase_(true),
|
|
redirectionFollowed_(true)
|
|
{
|
|
Setup();
|
|
}
|
|
|
|
|
|
HttpClient::HttpClient(const WebServiceParameters& service,
|
|
const std::string& uri) :
|
|
pimpl_(new PImpl),
|
|
verifyPeers_(true),
|
|
headersToLowerCase_(true),
|
|
redirectionFollowed_(true)
|
|
{
|
|
Setup();
|
|
|
|
if (service.GetUsername().size() != 0 &&
|
|
service.GetPassword().size() != 0)
|
|
{
|
|
SetCredentials(service.GetUsername().c_str(),
|
|
service.GetPassword().c_str());
|
|
}
|
|
|
|
if (!service.GetCertificateFile().empty())
|
|
{
|
|
SetClientCertificate(service.GetCertificateFile(),
|
|
service.GetCertificateKeyFile(),
|
|
service.GetCertificateKeyPassword());
|
|
}
|
|
|
|
SetPkcs11Enabled(service.IsPkcs11Enabled());
|
|
|
|
SetUrl(Toolbox::JoinUri(service.GetUrl(), uri));
|
|
|
|
for (WebServiceParameters::Dictionary::const_iterator
|
|
it = service.GetHttpHeaders().begin();
|
|
it != service.GetHttpHeaders().end(); ++it)
|
|
{
|
|
AddHeader(it->first, it->second);
|
|
}
|
|
|
|
if (service.HasTimeout())
|
|
{
|
|
SetTimeout(service.GetTimeout());
|
|
}
|
|
}
|
|
|
|
|
|
HttpClient::~HttpClient()
|
|
{
|
|
curl_easy_cleanup(pimpl_->curl_);
|
|
}
|
|
|
|
void HttpClient::SetUrl(const char *url)
|
|
{
|
|
url_ = std::string(url);
|
|
}
|
|
|
|
void HttpClient::SetUrl(const std::string &url)
|
|
{
|
|
url_ = url;
|
|
}
|
|
|
|
const std::string &HttpClient::GetUrl() const
|
|
{
|
|
return url_;
|
|
}
|
|
|
|
void HttpClient::SetMethod(HttpMethod method)
|
|
{
|
|
method_ = method;
|
|
}
|
|
|
|
HttpMethod HttpClient::GetMethod() const
|
|
{
|
|
return method_;
|
|
}
|
|
|
|
void HttpClient::SetTimeout(long seconds)
|
|
{
|
|
timeout_ = seconds;
|
|
}
|
|
|
|
long HttpClient::GetTimeout() const
|
|
{
|
|
return timeout_;
|
|
}
|
|
|
|
|
|
void HttpClient::AssignBody(const std::string& data)
|
|
{
|
|
body_ = data;
|
|
pimpl_->requestBody_.Clear();
|
|
hasExternalBody_ = false;
|
|
}
|
|
|
|
|
|
void HttpClient::AssignBody(const void* data,
|
|
size_t size)
|
|
{
|
|
if (size != 0 &&
|
|
data == NULL)
|
|
{
|
|
throw OrthancException(ErrorCode_NullPointer);
|
|
}
|
|
else
|
|
{
|
|
body_.assign(reinterpret_cast<const char*>(data), size);
|
|
pimpl_->requestBody_.Clear();
|
|
hasExternalBody_ = false;
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::SetBody(IRequestBody& body)
|
|
{
|
|
body_.clear();
|
|
pimpl_->requestBody_.SetBody(body);
|
|
hasExternalBody_ = false;
|
|
}
|
|
|
|
|
|
void HttpClient::SetExternalBody(const void* data,
|
|
size_t size)
|
|
{
|
|
if (size != 0 &&
|
|
data == NULL)
|
|
{
|
|
throw OrthancException(ErrorCode_NullPointer);
|
|
}
|
|
else
|
|
{
|
|
body_.clear();
|
|
pimpl_->requestBody_.Clear();
|
|
hasExternalBody_ = true;
|
|
externalBodyData_ = data;
|
|
externalBodySize_ = size;
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::SetExternalBody(const std::string& data)
|
|
{
|
|
SetExternalBody(data.empty() ? NULL : data.c_str(), data.size());
|
|
}
|
|
|
|
|
|
void HttpClient::ClearBody()
|
|
{
|
|
body_.clear();
|
|
pimpl_->requestBody_.Clear();
|
|
hasExternalBody_ = false;
|
|
}
|
|
|
|
|
|
void HttpClient::SetVerbose(bool isVerbose)
|
|
{
|
|
isVerbose_ = isVerbose;
|
|
|
|
if (isVerbose_)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1));
|
|
//CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_DEBUGFUNCTION, &CurlDebugCallback));
|
|
}
|
|
else
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0));
|
|
}
|
|
}
|
|
|
|
bool HttpClient::IsVerbose() const
|
|
{
|
|
return isVerbose_;
|
|
}
|
|
|
|
|
|
void HttpClient::AddHeader(const std::string& key,
|
|
const std::string& value)
|
|
{
|
|
if (key.empty())
|
|
{
|
|
throw OrthancException(ErrorCode_ParameterOutOfRange);
|
|
}
|
|
else
|
|
{
|
|
pimpl_->userHeaders_.AddHeader(key, value);
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::ClearHeaders()
|
|
{
|
|
pimpl_->userHeaders_.Clear();
|
|
}
|
|
|
|
|
|
bool HttpClient::ApplyInternal(CurlAnswer& answer)
|
|
{
|
|
CLOG(INFO, HTTP) << "New HTTP request to: " << url_ << " (timeout: "
|
|
<< boost::lexical_cast<std::string>(timeout_ <= 0 ? DEFAULT_HTTP_TIMEOUT : timeout_) << "s)";
|
|
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str()));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, &answer));
|
|
|
|
#if ORTHANC_ENABLE_SSL == 1
|
|
// Setup HTTPS-related options
|
|
|
|
if (verifyPeers_)
|
|
{
|
|
#if defined(CURLSSLOPT_NATIVE_CA) // from curl v 8.2.0
|
|
if (caCertificates_.empty()) // use native CA store (equivalent to --ca-native)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// use provided CA file (equivalent to --cacert)
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, caCertificates_.c_str()));
|
|
}
|
|
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 2)); // libcurl default is strict verifyhost
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1));
|
|
}
|
|
else
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 0));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0));
|
|
}
|
|
#endif
|
|
|
|
// Setup the HTTPS client certificate
|
|
if (!clientCertificateFile_.empty() &&
|
|
pkcs11Enabled_)
|
|
{
|
|
throw OrthancException(ErrorCode_ParameterOutOfRange,
|
|
"Cannot enable both client certificates and PKCS#11 authentication");
|
|
}
|
|
|
|
if (pkcs11Enabled_)
|
|
{
|
|
#if ORTHANC_ENABLE_PKCS11 == 1
|
|
if (GlobalParameters::GetInstance().IsPkcs11Initialized())
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLENGINE, Pkcs11::GetEngineIdentifier()));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "ENG"));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "ENG"));
|
|
}
|
|
else
|
|
{
|
|
throw OrthancException(ErrorCode_BadSequenceOfCalls,
|
|
"Cannot use PKCS#11 for a HTTPS request, "
|
|
"because it has not been initialized");
|
|
}
|
|
#else
|
|
throw OrthancException(ErrorCode_InternalError,
|
|
"This version of Orthanc is compiled without support for PKCS#11");
|
|
#endif
|
|
}
|
|
else if (!clientCertificateFile_.empty())
|
|
{
|
|
#if ORTHANC_ENABLE_SSL == 1
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "PEM"));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERT, clientCertificateFile_.c_str()));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_KEYPASSWD, clientCertificateKeyPassword_.c_str()));
|
|
|
|
// NB: If no "clientKeyFile_" is provided, the key must be
|
|
// prepended to the certificate file
|
|
if (!clientCertificateKeyFile_.empty())
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "PEM"));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEY, clientCertificateKeyFile_.c_str()));
|
|
}
|
|
#else
|
|
throw OrthancException(ErrorCode_InternalError,
|
|
"This version of Orthanc is compiled without OpenSSL support, "
|
|
"cannot use HTTPS client authentication");
|
|
#endif
|
|
}
|
|
|
|
// Reset the parameters from previous calls to Apply()
|
|
pimpl_->userHeaders_.Assign(pimpl_->curl_);
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 0L));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 0L));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, NULL));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0L));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, NULL));
|
|
|
|
if (redirectionFollowed_)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1L));
|
|
}
|
|
else
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 0L));
|
|
}
|
|
|
|
// Set timeouts
|
|
if (timeout_ <= 0)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, DEFAULT_HTTP_TIMEOUT));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, DEFAULT_HTTP_TIMEOUT));
|
|
}
|
|
else
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, timeout_));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, timeout_));
|
|
}
|
|
|
|
if (credentials_.size() != 0)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, credentials_.c_str()));
|
|
}
|
|
|
|
if (proxy_.size() != 0)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, proxy_.c_str()));
|
|
}
|
|
|
|
switch (method_)
|
|
{
|
|
case HttpMethod_Get:
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L));
|
|
break;
|
|
|
|
case HttpMethod_Post:
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L));
|
|
|
|
break;
|
|
|
|
case HttpMethod_Delete:
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE"));
|
|
break;
|
|
|
|
case HttpMethod_Put:
|
|
// http://stackoverflow.com/a/7570281/881731: Don't use
|
|
// CURLOPT_PUT if there is a body
|
|
|
|
// CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L));
|
|
|
|
curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "PUT"); /* !!! */
|
|
break;
|
|
|
|
default:
|
|
throw OrthancException(ErrorCode_InternalError);
|
|
}
|
|
|
|
if (method_ == HttpMethod_Post ||
|
|
method_ == HttpMethod_Put)
|
|
{
|
|
if (!pimpl_->userHeaders_.IsEmpty() &&
|
|
!pimpl_->userHeaders_.HasExpect())
|
|
{
|
|
CLOG(INFO, HTTP) << "For performance, the HTTP header \"Expect\" should be set to empty string in POST/PUT requests";
|
|
}
|
|
|
|
if (pimpl_->requestBody_.IsValid())
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READFUNCTION, CurlRequestBody::Callback));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READDATA, &pimpl_->requestBody_));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, -1L));
|
|
|
|
if (pimpl_->userHeaders_.IsEmpty())
|
|
{
|
|
pimpl_->defaultChunkedHeaders_.Assign(pimpl_->curl_);
|
|
}
|
|
else if (!pimpl_->userHeaders_.IsChunkedTransfer())
|
|
{
|
|
LOG(WARNING) << "The HTTP header \"Transfer-Encoding\" must be set to \"chunked\" "
|
|
<< "if streaming a chunked body in POST/PUT requests";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Disable possible previous stream transfers
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_READFUNCTION, NULL));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_UPLOAD, 0));
|
|
|
|
if (pimpl_->userHeaders_.IsChunkedTransfer())
|
|
{
|
|
LOG(WARNING) << "The HTTP header \"Transfer-Encoding\" must only be set "
|
|
<< "if streaming a chunked body in POST/PUT requests";
|
|
}
|
|
|
|
if (pimpl_->userHeaders_.IsEmpty())
|
|
{
|
|
pimpl_->defaultPostHeaders_.Assign(pimpl_->curl_);
|
|
}
|
|
|
|
if (hasExternalBody_)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, externalBodyData_));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, externalBodySize_));
|
|
}
|
|
else if (body_.size() > 0)
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str()));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size()));
|
|
}
|
|
else
|
|
{
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL));
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Do the actual request
|
|
CURLcode code;
|
|
long status = 0;
|
|
|
|
CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer));
|
|
|
|
const boost::posix_time::ptime start = boost::posix_time::microsec_clock::universal_time();
|
|
|
|
if (boost::starts_with(url_, "https://"))
|
|
{
|
|
code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status, url_);
|
|
}
|
|
else
|
|
{
|
|
code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status, url_);
|
|
}
|
|
|
|
const boost::posix_time::ptime end = boost::posix_time::microsec_clock::universal_time();
|
|
|
|
CLOG(INFO, HTTP) << "HTTP status code " << status << " in "
|
|
<< ((end - start).total_milliseconds()) << " ms after "
|
|
<< EnumerationToString(method_) << " request on: " << url_;
|
|
|
|
if (isVerbose_)
|
|
{
|
|
CLOG(INFO, HTTP) << "cURL status code: " << code;
|
|
}
|
|
|
|
CheckCode(code, url_); // throws on HTTP error
|
|
|
|
if (status == 0)
|
|
{
|
|
// This corresponds to a call to an inexistent host
|
|
lastStatus_ = HttpStatus_500_InternalServerError;
|
|
}
|
|
else
|
|
{
|
|
lastStatus_ = static_cast<HttpStatus>(status);
|
|
}
|
|
|
|
if (status >= 200 && status < 300)
|
|
{
|
|
return true; // Success
|
|
}
|
|
else
|
|
{
|
|
LOG(ERROR) << "Error in HTTP request, received HTTP status " << status
|
|
<< " (" << EnumerationToString(lastStatus_) << ") after "
|
|
<< EnumerationToString(method_) << " request on: " << url_;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool HttpClient::ApplyInternal(std::string& answerBody,
|
|
HttpHeaders* answerHeaders)
|
|
{
|
|
answerBody.clear();
|
|
|
|
DefaultAnswer answer;
|
|
|
|
if (answerHeaders != NULL)
|
|
{
|
|
answer.SetHeaders(*answerHeaders);
|
|
}
|
|
|
|
CurlAnswer wrapper(answer, headersToLowerCase_);
|
|
|
|
if (ApplyInternal(wrapper))
|
|
{
|
|
answer.FlattenBody(answerBody);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool HttpClient::ApplyInternal(Json::Value& answerBody,
|
|
HttpClient::HttpHeaders* answerHeaders)
|
|
{
|
|
std::string s;
|
|
if (ApplyInternal(s, answerHeaders))
|
|
{
|
|
return Toolbox::ReadJson(answerBody, s);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::SetCredentials(const char* username,
|
|
const char* password)
|
|
{
|
|
credentials_ = std::string(username) + ":" + std::string(password);
|
|
}
|
|
|
|
void HttpClient::SetProxy(const std::string &proxy)
|
|
{
|
|
proxy_ = proxy;
|
|
}
|
|
|
|
void HttpClient::SetHttpsVerifyPeers(bool verify)
|
|
{
|
|
verifyPeers_ = verify;
|
|
}
|
|
|
|
bool HttpClient::IsHttpsVerifyPeers() const
|
|
{
|
|
return verifyPeers_;
|
|
}
|
|
|
|
void HttpClient::SetHttpsCACertificates(const std::string &certificates)
|
|
{
|
|
caCertificates_ = certificates;
|
|
}
|
|
|
|
const std::string &HttpClient::GetHttpsCACertificates() const
|
|
{
|
|
return caCertificates_;
|
|
}
|
|
|
|
|
|
void HttpClient::ConfigureSsl(bool httpsVerifyPeers,
|
|
const std::string& httpsVerifyCertificates)
|
|
{
|
|
#if ORTHANC_ENABLE_SSL == 1
|
|
if (httpsVerifyPeers)
|
|
{
|
|
if (httpsVerifyCertificates.empty())
|
|
{
|
|
LOG(WARNING) << "No certificates are provided to validate peers. Orthanc will use the native CA store. "
|
|
<< "Set \"HttpsCACertificates\" if you need to do HTTPS requests and use custom CAs.";
|
|
}
|
|
else
|
|
{
|
|
LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << httpsVerifyCertificates;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled";
|
|
}
|
|
#endif
|
|
|
|
GlobalParameters::GetInstance().ConfigureSsl(httpsVerifyPeers, httpsVerifyCertificates);
|
|
}
|
|
|
|
|
|
void HttpClient::GlobalInitialize()
|
|
{
|
|
#if ORTHANC_ENABLE_SSL == 1
|
|
CheckCode(curl_global_init(CURL_GLOBAL_ALL));
|
|
#else
|
|
CheckCode(curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL));
|
|
#endif
|
|
}
|
|
|
|
|
|
void HttpClient::GlobalFinalize()
|
|
{
|
|
curl_global_cleanup();
|
|
|
|
#if ORTHANC_ENABLE_PKCS11 == 1
|
|
Pkcs11::Finalize();
|
|
#endif
|
|
}
|
|
|
|
|
|
void HttpClient::SetDefaultVerbose(bool verbose)
|
|
{
|
|
GlobalParameters::GetInstance().SetDefaultVerbose(verbose);
|
|
}
|
|
|
|
|
|
void HttpClient::SetDefaultProxy(const std::string& proxy)
|
|
{
|
|
GlobalParameters::GetInstance().SetDefaultProxy(proxy);
|
|
}
|
|
|
|
|
|
void HttpClient::SetDefaultTimeout(long timeout)
|
|
{
|
|
GlobalParameters::GetInstance().SetDefaultTimeout(timeout);
|
|
}
|
|
|
|
|
|
bool HttpClient::Apply(IAnswer& answer)
|
|
{
|
|
CurlAnswer wrapper(answer, headersToLowerCase_);
|
|
return ApplyInternal(wrapper);
|
|
}
|
|
|
|
bool HttpClient::Apply(std::string &answerBody)
|
|
{
|
|
return ApplyInternal(answerBody, NULL);
|
|
}
|
|
|
|
bool HttpClient::Apply(Json::Value &answerBody)
|
|
{
|
|
return ApplyInternal(answerBody, NULL);
|
|
}
|
|
|
|
bool HttpClient::Apply(std::string &answerBody,
|
|
HttpClient::HttpHeaders &answerHeaders)
|
|
{
|
|
return ApplyInternal(answerBody, &answerHeaders);
|
|
}
|
|
|
|
bool HttpClient::Apply(Json::Value &answerBody,
|
|
HttpClient::HttpHeaders &answerHeaders)
|
|
{
|
|
return ApplyInternal(answerBody, &answerHeaders);
|
|
}
|
|
|
|
HttpStatus HttpClient::GetLastStatus() const
|
|
{
|
|
return lastStatus_;
|
|
}
|
|
|
|
|
|
void HttpClient::ApplyAndThrowException(IAnswer& answer)
|
|
{
|
|
CurlAnswer wrapper(answer, headersToLowerCase_);
|
|
|
|
if (!ApplyInternal(wrapper))
|
|
{
|
|
ThrowException(GetLastStatus());
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::ApplyAndThrowException(std::string& answerBody)
|
|
{
|
|
if (!Apply(answerBody))
|
|
{
|
|
ThrowException(GetLastStatus());
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::ApplyAndThrowException(Json::Value& answerBody)
|
|
{
|
|
if (!Apply(answerBody))
|
|
{
|
|
ThrowException(GetLastStatus());
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::ApplyAndThrowException(std::string& answerBody,
|
|
HttpHeaders& answerHeaders)
|
|
{
|
|
if (!Apply(answerBody, answerHeaders))
|
|
{
|
|
ThrowException(GetLastStatus());
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::ApplyAndThrowException(Json::Value& answerBody,
|
|
HttpHeaders& answerHeaders)
|
|
{
|
|
if (!Apply(answerBody, answerHeaders))
|
|
{
|
|
ThrowException(GetLastStatus());
|
|
}
|
|
}
|
|
|
|
|
|
void HttpClient::SetClientCertificate(const std::string& certificateFile,
|
|
const std::string& certificateKeyFile,
|
|
const std::string& certificateKeyPassword)
|
|
{
|
|
if (certificateFile.empty())
|
|
{
|
|
throw OrthancException(ErrorCode_ParameterOutOfRange);
|
|
}
|
|
|
|
if (!SystemToolbox::IsRegularFile(certificateFile))
|
|
{
|
|
throw OrthancException(ErrorCode_InexistentFile,
|
|
"Cannot open certificate file: " + certificateFile);
|
|
}
|
|
|
|
if (!certificateKeyFile.empty() &&
|
|
!SystemToolbox::IsRegularFile(certificateKeyFile))
|
|
{
|
|
throw OrthancException(ErrorCode_InexistentFile,
|
|
"Cannot open key file: " + certificateKeyFile);
|
|
}
|
|
|
|
clientCertificateFile_ = certificateFile;
|
|
clientCertificateKeyFile_ = certificateKeyFile;
|
|
clientCertificateKeyPassword_ = certificateKeyPassword;
|
|
}
|
|
|
|
void HttpClient::SetPkcs11Enabled(bool enabled)
|
|
{
|
|
pkcs11Enabled_ = enabled;
|
|
}
|
|
|
|
bool HttpClient::IsPkcs11Enabled() const
|
|
{
|
|
return pkcs11Enabled_;
|
|
}
|
|
|
|
const std::string &HttpClient::GetClientCertificateFile() const
|
|
{
|
|
return clientCertificateFile_;
|
|
}
|
|
|
|
const std::string &HttpClient::GetClientCertificateKeyFile() const
|
|
{
|
|
return clientCertificateKeyFile_;
|
|
}
|
|
|
|
const std::string &HttpClient::GetClientCertificateKeyPassword() const
|
|
{
|
|
return clientCertificateKeyPassword_;
|
|
}
|
|
|
|
void HttpClient::SetConvertHeadersToLowerCase(bool lowerCase)
|
|
{
|
|
headersToLowerCase_ = lowerCase;
|
|
}
|
|
|
|
bool HttpClient::IsConvertHeadersToLowerCase() const
|
|
{
|
|
return headersToLowerCase_;
|
|
}
|
|
|
|
void HttpClient::SetRedirectionFollowed(bool follow)
|
|
{
|
|
redirectionFollowed_ = follow;
|
|
}
|
|
|
|
bool HttpClient::IsRedirectionFollowed() const
|
|
{
|
|
return redirectionFollowed_;
|
|
}
|
|
|
|
|
|
void HttpClient::InitializePkcs11(const std::string& module,
|
|
const std::string& pin,
|
|
bool verbose)
|
|
{
|
|
#if ORTHANC_ENABLE_PKCS11 == 1
|
|
CLOG(INFO, HTTP) << "Initializing PKCS#11 using " << module
|
|
<< (pin.empty() ? " (no PIN provided)" : " (PIN is provided)");
|
|
GlobalParameters::GetInstance().InitializePkcs11(module, pin, verbose);
|
|
#else
|
|
throw OrthancException(ErrorCode_InternalError,
|
|
"This version of Orthanc is compiled without support for PKCS#11");
|
|
#endif
|
|
}
|
|
}
|