Mercurial > hg > orthanc-wsi
comparison Resources/Orthanc/Core/HttpClient.cpp @ 59:7a3853d51c45
Move "Framework/Orthanc/" as "Resources/Orthanc/"
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 25 Nov 2016 16:34:34 +0100 |
parents | Framework/Orthanc/Core/HttpClient.cpp@9621fd6f17c9 |
children | 5b127ab0080b |
comparison
equal
deleted
inserted
replaced
58:35468714a38e | 59:7a3853d51c45 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #include "PrecompiledHeaders.h" | |
34 #include "HttpClient.h" | |
35 | |
36 #include "Toolbox.h" | |
37 #include "OrthancException.h" | |
38 #include "Logging.h" | |
39 #include "ChunkedBuffer.h" | |
40 #include "SystemToolbox.h" | |
41 | |
42 #include <string.h> | |
43 #include <curl/curl.h> | |
44 #include <boost/algorithm/string/predicate.hpp> | |
45 #include <boost/thread/mutex.hpp> | |
46 | |
47 | |
48 #if ORTHANC_ENABLE_SSL == 1 | |
49 // For OpenSSL initialization and finalization | |
50 # include <openssl/conf.h> | |
51 # include <openssl/engine.h> | |
52 # include <openssl/err.h> | |
53 # include <openssl/evp.h> | |
54 # include <openssl/ssl.h> | |
55 #endif | |
56 | |
57 | |
58 #if ORTHANC_ENABLE_PKCS11 == 1 | |
59 # include "Pkcs11.h" | |
60 #endif | |
61 | |
62 | |
63 extern "C" | |
64 { | |
65 static CURLcode GetHttpStatus(CURLcode code, CURL* curl, long* status) | |
66 { | |
67 if (code == CURLE_OK) | |
68 { | |
69 code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, status); | |
70 return code; | |
71 } | |
72 else | |
73 { | |
74 *status = 0; | |
75 return code; | |
76 } | |
77 } | |
78 | |
79 // This is a dummy wrapper function to suppress any OpenSSL-related | |
80 // problem in valgrind. Inlining is prevented. | |
81 #if defined(__GNUC__) || defined(__clang__) | |
82 __attribute__((noinline)) | |
83 #endif | |
84 static CURLcode OrthancHttpClientPerformSSL(CURL* curl, long* status) | |
85 { | |
86 return GetHttpStatus(curl_easy_perform(curl), curl, status); | |
87 } | |
88 } | |
89 | |
90 | |
91 | |
92 namespace Orthanc | |
93 { | |
94 class HttpClient::GlobalParameters | |
95 { | |
96 private: | |
97 boost::mutex mutex_; | |
98 bool httpsVerifyPeers_; | |
99 std::string httpsCACertificates_; | |
100 std::string proxy_; | |
101 long timeout_; | |
102 | |
103 GlobalParameters() : | |
104 httpsVerifyPeers_(true), | |
105 timeout_(0) | |
106 { | |
107 } | |
108 | |
109 public: | |
110 // Singleton pattern | |
111 static GlobalParameters& GetInstance() | |
112 { | |
113 static GlobalParameters parameters; | |
114 return parameters; | |
115 } | |
116 | |
117 void ConfigureSsl(bool httpsVerifyPeers, | |
118 const std::string& httpsCACertificates) | |
119 { | |
120 boost::mutex::scoped_lock lock(mutex_); | |
121 httpsVerifyPeers_ = httpsVerifyPeers; | |
122 httpsCACertificates_ = httpsCACertificates; | |
123 } | |
124 | |
125 void GetSslConfiguration(bool& httpsVerifyPeers, | |
126 std::string& httpsCACertificates) | |
127 { | |
128 boost::mutex::scoped_lock lock(mutex_); | |
129 httpsVerifyPeers = httpsVerifyPeers_; | |
130 httpsCACertificates = httpsCACertificates_; | |
131 } | |
132 | |
133 void SetDefaultProxy(const std::string& proxy) | |
134 { | |
135 LOG(INFO) << "Setting the default proxy for HTTP client connections: " << proxy; | |
136 | |
137 { | |
138 boost::mutex::scoped_lock lock(mutex_); | |
139 proxy_ = proxy; | |
140 } | |
141 } | |
142 | |
143 void GetDefaultProxy(std::string& target) | |
144 { | |
145 boost::mutex::scoped_lock lock(mutex_); | |
146 target = proxy_; | |
147 } | |
148 | |
149 void SetDefaultTimeout(long seconds) | |
150 { | |
151 LOG(INFO) << "Setting the default timeout for HTTP client connections: " << seconds << " seconds"; | |
152 | |
153 { | |
154 boost::mutex::scoped_lock lock(mutex_); | |
155 timeout_ = seconds; | |
156 } | |
157 } | |
158 | |
159 long GetDefaultTimeout() | |
160 { | |
161 boost::mutex::scoped_lock lock(mutex_); | |
162 return timeout_; | |
163 } | |
164 | |
165 #if ORTHANC_ENABLE_PKCS11 == 1 | |
166 bool IsPkcs11Initialized() | |
167 { | |
168 boost::mutex::scoped_lock lock(mutex_); | |
169 return Pkcs11::IsInitialized(); | |
170 } | |
171 | |
172 void InitializePkcs11(const std::string& module, | |
173 const std::string& pin, | |
174 bool verbose) | |
175 { | |
176 boost::mutex::scoped_lock lock(mutex_); | |
177 Pkcs11::Initialize(module, pin, verbose); | |
178 } | |
179 #endif | |
180 }; | |
181 | |
182 | |
183 struct HttpClient::PImpl | |
184 { | |
185 CURL* curl_; | |
186 struct curl_slist *defaultPostHeaders_; | |
187 struct curl_slist *userHeaders_; | |
188 }; | |
189 | |
190 | |
191 static void ThrowException(HttpStatus status) | |
192 { | |
193 switch (status) | |
194 { | |
195 case HttpStatus_400_BadRequest: | |
196 throw OrthancException(ErrorCode_BadRequest); | |
197 | |
198 case HttpStatus_401_Unauthorized: | |
199 case HttpStatus_403_Forbidden: | |
200 throw OrthancException(ErrorCode_Unauthorized); | |
201 | |
202 case HttpStatus_404_NotFound: | |
203 throw OrthancException(ErrorCode_UnknownResource); | |
204 | |
205 default: | |
206 throw OrthancException(ErrorCode_NetworkProtocol); | |
207 } | |
208 } | |
209 | |
210 | |
211 static CURLcode CheckCode(CURLcode code) | |
212 { | |
213 if (code == CURLE_NOT_BUILT_IN) | |
214 { | |
215 LOG(ERROR) << "Your libcurl does not contain a required feature, " | |
216 << "please recompile Orthanc with -DUSE_SYSTEM_CURL=OFF"; | |
217 throw OrthancException(ErrorCode_InternalError); | |
218 } | |
219 | |
220 if (code != CURLE_OK) | |
221 { | |
222 LOG(ERROR) << "libCURL error: " + std::string(curl_easy_strerror(code)); | |
223 throw OrthancException(ErrorCode_NetworkProtocol); | |
224 } | |
225 | |
226 return code; | |
227 } | |
228 | |
229 | |
230 static size_t CurlBodyCallback(void *buffer, size_t size, size_t nmemb, void *payload) | |
231 { | |
232 ChunkedBuffer& target = *(static_cast<ChunkedBuffer*>(payload)); | |
233 | |
234 size_t length = size * nmemb; | |
235 if (length == 0) | |
236 { | |
237 return 0; | |
238 } | |
239 else | |
240 { | |
241 target.AddChunk(buffer, length); | |
242 return length; | |
243 } | |
244 } | |
245 | |
246 | |
247 struct CurlHeaderParameters | |
248 { | |
249 bool lowerCase_; | |
250 HttpClient::HttpHeaders* headers_; | |
251 }; | |
252 | |
253 | |
254 static size_t CurlHeaderCallback(void *buffer, size_t size, size_t nmemb, void *payload) | |
255 { | |
256 CurlHeaderParameters& parameters = *(static_cast<CurlHeaderParameters*>(payload)); | |
257 assert(parameters.headers_ != NULL); | |
258 | |
259 size_t length = size * nmemb; | |
260 if (length == 0) | |
261 { | |
262 return 0; | |
263 } | |
264 else | |
265 { | |
266 std::string s(reinterpret_cast<const char*>(buffer), length); | |
267 std::size_t colon = s.find(':'); | |
268 std::size_t eol = s.find("\r\n"); | |
269 if (colon != std::string::npos && | |
270 eol != std::string::npos) | |
271 { | |
272 std::string tmp(s.substr(0, colon)); | |
273 | |
274 if (parameters.lowerCase_) | |
275 { | |
276 Toolbox::ToLowerCase(tmp); | |
277 } | |
278 | |
279 std::string key = Toolbox::StripSpaces(tmp); | |
280 | |
281 if (!key.empty()) | |
282 { | |
283 std::string value = Toolbox::StripSpaces(s.substr(colon + 1, eol)); | |
284 (*parameters.headers_) [key] = value; | |
285 } | |
286 } | |
287 | |
288 return length; | |
289 } | |
290 } | |
291 | |
292 | |
293 void HttpClient::Setup() | |
294 { | |
295 pimpl_->userHeaders_ = NULL; | |
296 pimpl_->defaultPostHeaders_ = NULL; | |
297 if ((pimpl_->defaultPostHeaders_ = curl_slist_append(pimpl_->defaultPostHeaders_, "Expect:")) == NULL) | |
298 { | |
299 throw OrthancException(ErrorCode_NotEnoughMemory); | |
300 } | |
301 | |
302 pimpl_->curl_ = curl_easy_init(); | |
303 if (!pimpl_->curl_) | |
304 { | |
305 curl_slist_free_all(pimpl_->defaultPostHeaders_); | |
306 throw OrthancException(ErrorCode_NotEnoughMemory); | |
307 } | |
308 | |
309 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlBodyCallback)); | |
310 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0)); | |
311 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1)); | |
312 | |
313 // This fixes the "longjmp causes uninitialized stack frame" crash | |
314 // that happens on modern Linux versions. | |
315 // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame | |
316 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOSIGNAL, 1)); | |
317 | |
318 url_ = ""; | |
319 method_ = HttpMethod_Get; | |
320 lastStatus_ = HttpStatus_200_Ok; | |
321 isVerbose_ = false; | |
322 timeout_ = GlobalParameters::GetInstance().GetDefaultTimeout(); | |
323 GlobalParameters::GetInstance().GetDefaultProxy(proxy_); | |
324 GlobalParameters::GetInstance().GetSslConfiguration(verifyPeers_, caCertificates_); | |
325 } | |
326 | |
327 | |
328 HttpClient::HttpClient() : | |
329 pimpl_(new PImpl), | |
330 verifyPeers_(true), | |
331 pkcs11Enabled_(false), | |
332 headersToLowerCase_(true), | |
333 redirectionFollowed_(true) | |
334 { | |
335 Setup(); | |
336 } | |
337 | |
338 | |
339 HttpClient::HttpClient(const WebServiceParameters& service, | |
340 const std::string& uri) : | |
341 pimpl_(new PImpl), | |
342 verifyPeers_(true), | |
343 headersToLowerCase_(true), | |
344 redirectionFollowed_(true) | |
345 { | |
346 Setup(); | |
347 | |
348 if (service.GetUsername().size() != 0 && | |
349 service.GetPassword().size() != 0) | |
350 { | |
351 SetCredentials(service.GetUsername().c_str(), | |
352 service.GetPassword().c_str()); | |
353 } | |
354 | |
355 if (!service.GetCertificateFile().empty()) | |
356 { | |
357 SetClientCertificate(service.GetCertificateFile(), | |
358 service.GetCertificateKeyFile(), | |
359 service.GetCertificateKeyPassword()); | |
360 } | |
361 | |
362 SetPkcs11Enabled(service.IsPkcs11Enabled()); | |
363 | |
364 SetUrl(service.GetUrl() + uri); | |
365 } | |
366 | |
367 | |
368 HttpClient::~HttpClient() | |
369 { | |
370 curl_easy_cleanup(pimpl_->curl_); | |
371 curl_slist_free_all(pimpl_->defaultPostHeaders_); | |
372 ClearHeaders(); | |
373 } | |
374 | |
375 | |
376 void HttpClient::SetVerbose(bool isVerbose) | |
377 { | |
378 isVerbose_ = isVerbose; | |
379 | |
380 if (isVerbose_) | |
381 { | |
382 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1)); | |
383 } | |
384 else | |
385 { | |
386 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0)); | |
387 } | |
388 } | |
389 | |
390 | |
391 void HttpClient::AddHeader(const std::string& key, | |
392 const std::string& value) | |
393 { | |
394 if (key.empty()) | |
395 { | |
396 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
397 } | |
398 | |
399 std::string s = key + ": " + value; | |
400 | |
401 if ((pimpl_->userHeaders_ = curl_slist_append(pimpl_->userHeaders_, s.c_str())) == NULL) | |
402 { | |
403 throw OrthancException(ErrorCode_NotEnoughMemory); | |
404 } | |
405 } | |
406 | |
407 | |
408 void HttpClient::ClearHeaders() | |
409 { | |
410 if (pimpl_->userHeaders_ != NULL) | |
411 { | |
412 curl_slist_free_all(pimpl_->userHeaders_); | |
413 pimpl_->userHeaders_ = NULL; | |
414 } | |
415 } | |
416 | |
417 | |
418 bool HttpClient::ApplyInternal(std::string& answerBody, | |
419 HttpHeaders* answerHeaders) | |
420 { | |
421 answerBody.clear(); | |
422 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str())); | |
423 | |
424 CurlHeaderParameters headerParameters; | |
425 | |
426 if (answerHeaders == NULL) | |
427 { | |
428 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, NULL)); | |
429 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, NULL)); | |
430 } | |
431 else | |
432 { | |
433 headerParameters.lowerCase_ = headersToLowerCase_; | |
434 headerParameters.headers_ = answerHeaders; | |
435 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERFUNCTION, &CurlHeaderCallback)); | |
436 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADERDATA, &headerParameters)); | |
437 } | |
438 | |
439 #if ORTHANC_ENABLE_SSL == 1 | |
440 // Setup HTTPS-related options | |
441 | |
442 if (verifyPeers_) | |
443 { | |
444 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CAINFO, caCertificates_.c_str())); | |
445 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 2)); // libcurl default is strict verifyhost | |
446 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 1)); | |
447 } | |
448 else | |
449 { | |
450 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYHOST, 0)); | |
451 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0)); | |
452 } | |
453 #endif | |
454 | |
455 // Setup the HTTPS client certificate | |
456 if (!clientCertificateFile_.empty() && | |
457 pkcs11Enabled_) | |
458 { | |
459 LOG(ERROR) << "Cannot enable both client certificates and PKCS#11 authentication"; | |
460 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
461 } | |
462 | |
463 if (pkcs11Enabled_) | |
464 { | |
465 #if ORTHANC_ENABLE_PKCS11 == 1 | |
466 if (GlobalParameters::GetInstance().IsPkcs11Initialized()) | |
467 { | |
468 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLENGINE, Pkcs11::GetEngineIdentifier())); | |
469 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "ENG")); | |
470 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "ENG")); | |
471 } | |
472 else | |
473 { | |
474 LOG(ERROR) << "Cannot use PKCS#11 for a HTTPS request, because it has not been initialized"; | |
475 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
476 } | |
477 #else | |
478 LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS#11"; | |
479 throw OrthancException(ErrorCode_InternalError); | |
480 #endif | |
481 } | |
482 else if (!clientCertificateFile_.empty()) | |
483 { | |
484 #if ORTHANC_ENABLE_SSL == 1 | |
485 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERTTYPE, "PEM")); | |
486 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLCERT, clientCertificateFile_.c_str())); | |
487 | |
488 if (!clientCertificateKeyPassword_.empty()) | |
489 { | |
490 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_KEYPASSWD, clientCertificateKeyPassword_.c_str())); | |
491 } | |
492 | |
493 // NB: If no "clientKeyFile_" is provided, the key must be | |
494 // prepended to the certificate file | |
495 if (!clientCertificateKeyFile_.empty()) | |
496 { | |
497 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEYTYPE, "PEM")); | |
498 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSLKEY, clientCertificateKeyFile_.c_str())); | |
499 } | |
500 #else | |
501 LOG(ERROR) << "This version of Orthanc is compiled without OpenSSL support, cannot use HTTPS client authentication"; | |
502 throw OrthancException(ErrorCode_InternalError); | |
503 #endif | |
504 } | |
505 | |
506 // Reset the parameters from previous calls to Apply() | |
507 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->userHeaders_)); | |
508 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 0L)); | |
509 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 0L)); | |
510 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 0L)); | |
511 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, NULL)); | |
512 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); | |
513 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0L)); | |
514 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, NULL)); | |
515 | |
516 if (redirectionFollowed_) | |
517 { | |
518 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1L)); | |
519 } | |
520 else | |
521 { | |
522 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 0L)); | |
523 } | |
524 | |
525 // Set timeouts | |
526 if (timeout_ <= 0) | |
527 { | |
528 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, 10)); /* default: 10 seconds */ | |
529 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, 10)); /* default: 10 seconds */ | |
530 } | |
531 else | |
532 { | |
533 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_TIMEOUT, timeout_)); | |
534 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CONNECTTIMEOUT, timeout_)); | |
535 } | |
536 | |
537 if (credentials_.size() != 0) | |
538 { | |
539 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, credentials_.c_str())); | |
540 } | |
541 | |
542 if (proxy_.size() != 0) | |
543 { | |
544 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PROXY, proxy_.c_str())); | |
545 } | |
546 | |
547 switch (method_) | |
548 { | |
549 case HttpMethod_Get: | |
550 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L)); | |
551 break; | |
552 | |
553 case HttpMethod_Post: | |
554 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L)); | |
555 | |
556 if (pimpl_->userHeaders_ == NULL) | |
557 { | |
558 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->defaultPostHeaders_)); | |
559 } | |
560 | |
561 break; | |
562 | |
563 case HttpMethod_Delete: | |
564 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L)); | |
565 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE")); | |
566 break; | |
567 | |
568 case HttpMethod_Put: | |
569 // http://stackoverflow.com/a/7570281/881731: Don't use | |
570 // CURLOPT_PUT if there is a body | |
571 | |
572 // CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L)); | |
573 | |
574 curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "PUT"); /* !!! */ | |
575 | |
576 if (pimpl_->userHeaders_ == NULL) | |
577 { | |
578 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->defaultPostHeaders_)); | |
579 } | |
580 | |
581 break; | |
582 | |
583 default: | |
584 throw OrthancException(ErrorCode_InternalError); | |
585 } | |
586 | |
587 | |
588 if (method_ == HttpMethod_Post || | |
589 method_ == HttpMethod_Put) | |
590 { | |
591 if (body_.size() > 0) | |
592 { | |
593 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, body_.c_str())); | |
594 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, body_.size())); | |
595 } | |
596 else | |
597 { | |
598 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL)); | |
599 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0)); | |
600 } | |
601 } | |
602 | |
603 | |
604 // Do the actual request | |
605 CURLcode code; | |
606 long status = 0; | |
607 | |
608 ChunkedBuffer buffer; | |
609 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &buffer)); | |
610 | |
611 if (boost::starts_with(url_, "https://")) | |
612 { | |
613 code = OrthancHttpClientPerformSSL(pimpl_->curl_, &status); | |
614 } | |
615 else | |
616 { | |
617 code = GetHttpStatus(curl_easy_perform(pimpl_->curl_), pimpl_->curl_, &status); | |
618 } | |
619 | |
620 CheckCode(code); | |
621 | |
622 if (status == 0) | |
623 { | |
624 // This corresponds to a call to an inexistent host | |
625 lastStatus_ = HttpStatus_500_InternalServerError; | |
626 } | |
627 else | |
628 { | |
629 lastStatus_ = static_cast<HttpStatus>(status); | |
630 } | |
631 | |
632 bool success = (status >= 200 && status < 300); | |
633 | |
634 if (success) | |
635 { | |
636 buffer.Flatten(answerBody); | |
637 } | |
638 else | |
639 { | |
640 answerBody.clear(); | |
641 LOG(INFO) << "Error in HTTP request, received HTTP status " << status | |
642 << " (" << EnumerationToString(lastStatus_) << ")"; | |
643 } | |
644 | |
645 return success; | |
646 } | |
647 | |
648 | |
649 bool HttpClient::ApplyInternal(Json::Value& answerBody, | |
650 HttpClient::HttpHeaders* answerHeaders) | |
651 { | |
652 std::string s; | |
653 if (ApplyInternal(s, answerHeaders)) | |
654 { | |
655 Json::Reader reader; | |
656 return reader.parse(s, answerBody); | |
657 } | |
658 else | |
659 { | |
660 return false; | |
661 } | |
662 } | |
663 | |
664 | |
665 void HttpClient::SetCredentials(const char* username, | |
666 const char* password) | |
667 { | |
668 credentials_ = std::string(username) + ":" + std::string(password); | |
669 } | |
670 | |
671 | |
672 void HttpClient::ConfigureSsl(bool httpsVerifyPeers, | |
673 const std::string& httpsVerifyCertificates) | |
674 { | |
675 #if ORTHANC_ENABLE_SSL == 1 | |
676 if (httpsVerifyPeers) | |
677 { | |
678 if (httpsVerifyCertificates.empty()) | |
679 { | |
680 LOG(WARNING) << "No certificates are provided to validate peers, " | |
681 << "set \"HttpsCACertificates\" if you need to do HTTPS requests"; | |
682 } | |
683 else | |
684 { | |
685 LOG(WARNING) << "HTTPS will use the CA certificates from this file: " << httpsVerifyCertificates; | |
686 } | |
687 } | |
688 else | |
689 { | |
690 LOG(WARNING) << "The verification of the peers in HTTPS requests is disabled"; | |
691 } | |
692 #endif | |
693 | |
694 GlobalParameters::GetInstance().ConfigureSsl(httpsVerifyPeers, httpsVerifyCertificates); | |
695 } | |
696 | |
697 | |
698 void HttpClient::GlobalInitialize() | |
699 { | |
700 #if ORTHANC_ENABLE_SSL == 1 | |
701 CheckCode(curl_global_init(CURL_GLOBAL_ALL)); | |
702 #else | |
703 CheckCode(curl_global_init(CURL_GLOBAL_ALL & ~CURL_GLOBAL_SSL)); | |
704 #endif | |
705 } | |
706 | |
707 | |
708 void HttpClient::GlobalFinalize() | |
709 { | |
710 curl_global_cleanup(); | |
711 | |
712 #if ORTHANC_ENABLE_PKCS11 == 1 | |
713 Pkcs11::Finalize(); | |
714 #endif | |
715 } | |
716 | |
717 | |
718 void HttpClient::SetDefaultProxy(const std::string& proxy) | |
719 { | |
720 GlobalParameters::GetInstance().SetDefaultProxy(proxy); | |
721 } | |
722 | |
723 | |
724 void HttpClient::SetDefaultTimeout(long timeout) | |
725 { | |
726 GlobalParameters::GetInstance().SetDefaultTimeout(timeout); | |
727 } | |
728 | |
729 | |
730 void HttpClient::ApplyAndThrowException(std::string& answerBody) | |
731 { | |
732 if (!Apply(answerBody)) | |
733 { | |
734 ThrowException(GetLastStatus()); | |
735 } | |
736 } | |
737 | |
738 | |
739 void HttpClient::ApplyAndThrowException(Json::Value& answerBody) | |
740 { | |
741 if (!Apply(answerBody)) | |
742 { | |
743 ThrowException(GetLastStatus()); | |
744 } | |
745 } | |
746 | |
747 | |
748 void HttpClient::ApplyAndThrowException(std::string& answerBody, | |
749 HttpHeaders& answerHeaders) | |
750 { | |
751 if (!Apply(answerBody, answerHeaders)) | |
752 { | |
753 ThrowException(GetLastStatus()); | |
754 } | |
755 } | |
756 | |
757 | |
758 void HttpClient::ApplyAndThrowException(Json::Value& answerBody, | |
759 HttpHeaders& answerHeaders) | |
760 { | |
761 if (!Apply(answerBody, answerHeaders)) | |
762 { | |
763 ThrowException(GetLastStatus()); | |
764 } | |
765 } | |
766 | |
767 | |
768 void HttpClient::SetClientCertificate(const std::string& certificateFile, | |
769 const std::string& certificateKeyFile, | |
770 const std::string& certificateKeyPassword) | |
771 { | |
772 if (certificateFile.empty()) | |
773 { | |
774 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
775 } | |
776 | |
777 if (!SystemToolbox::IsRegularFile(certificateFile)) | |
778 { | |
779 LOG(ERROR) << "Cannot open certificate file: " << certificateFile; | |
780 throw OrthancException(ErrorCode_InexistentFile); | |
781 } | |
782 | |
783 if (!certificateKeyFile.empty() && | |
784 !SystemToolbox::IsRegularFile(certificateKeyFile)) | |
785 { | |
786 LOG(ERROR) << "Cannot open key file: " << certificateKeyFile; | |
787 throw OrthancException(ErrorCode_InexistentFile); | |
788 } | |
789 | |
790 clientCertificateFile_ = certificateFile; | |
791 clientCertificateKeyFile_ = certificateKeyFile; | |
792 clientCertificateKeyPassword_ = certificateKeyPassword; | |
793 } | |
794 | |
795 | |
796 void HttpClient::InitializePkcs11(const std::string& module, | |
797 const std::string& pin, | |
798 bool verbose) | |
799 { | |
800 #if ORTHANC_ENABLE_PKCS11 == 1 | |
801 LOG(INFO) << "Initializing PKCS#11 using " << module | |
802 << (pin.empty() ? " (no PIN provided)" : " (PIN is provided)"); | |
803 GlobalParameters::GetInstance().InitializePkcs11(module, pin, verbose); | |
804 #else | |
805 LOG(ERROR) << "This version of Orthanc is compiled without support for PKCS#11"; | |
806 throw OrthancException(ErrorCode_InternalError); | |
807 #endif | |
808 } | |
809 | |
810 | |
811 void HttpClient::InitializeOpenSsl() | |
812 { | |
813 #if ORTHANC_ENABLE_SSL == 1 | |
814 // https://wiki.openssl.org/index.php/Library_Initialization | |
815 SSL_library_init(); | |
816 SSL_load_error_strings(); | |
817 OpenSSL_add_all_algorithms(); | |
818 ERR_load_crypto_strings(); | |
819 #endif | |
820 } | |
821 | |
822 | |
823 void HttpClient::FinalizeOpenSsl() | |
824 { | |
825 #if ORTHANC_ENABLE_SSL == 1 | |
826 // Finalize OpenSSL | |
827 // https://wiki.openssl.org/index.php/Library_Initialization#Cleanup | |
828 FIPS_mode_set(0); | |
829 ENGINE_cleanup(); | |
830 CONF_modules_unload(1); | |
831 EVP_cleanup(); | |
832 CRYPTO_cleanup_all_ex_data(); | |
833 ERR_remove_state(0); | |
834 ERR_free_strings(); | |
835 #endif | |
836 } | |
837 } |