comparison Core/HttpClient.cpp @ 476:4aae0261515e

move HttpClient
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 15 Jul 2013 17:37:24 +0200
parents OrthancCppClient/HttpClient.cpp@72cca077abf8
children 6f8ae46ed90e
comparison
equal deleted inserted replaced
475:72cca077abf8 476:4aae0261515e
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2013 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * Permission is hereby granted, free of charge, to any person
7 * obtaining a copy of this software and associated documentation
8 * files (the "Software"), to deal in the Software without
9 * restriction, including without limitation the rights to use, copy,
10 * modify, merge, publish, distribute, sublicense, and/or sell copies
11 * of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 **/
26
27
28 #include "HttpClient.h"
29
30 #include "../Core/Toolbox.h"
31 #include "../Core/OrthancException.h"
32
33 #include <string.h>
34 #include <curl/curl.h>
35
36
37 namespace Orthanc
38 {
39 struct HttpClient::PImpl
40 {
41 CURL* curl_;
42 struct curl_slist *postHeaders_;
43 };
44
45
46 static CURLcode CheckCode(CURLcode code)
47 {
48 if (code != CURLE_OK)
49 {
50 throw OrthancException("libCURL error: " + std::string(curl_easy_strerror(code)));
51 }
52
53 return code;
54 }
55
56
57 static size_t CurlCallback(void *buffer, size_t size, size_t nmemb, void *payload)
58 {
59 std::string& target = *(static_cast<std::string*>(payload));
60
61 size_t length = size * nmemb;
62 if (length == 0)
63 return 0;
64
65 size_t pos = target.size();
66
67 target.resize(pos + length);
68 memcpy(&target.at(pos), buffer, length);
69
70 return length;
71 }
72
73
74 void HttpClient::Setup()
75 {
76 pimpl_->postHeaders_ = NULL;
77 if ((pimpl_->postHeaders_ = curl_slist_append(pimpl_->postHeaders_, "Expect:")) == NULL)
78 {
79 throw OrthancException(ErrorCode_NotEnoughMemory);
80 }
81
82 pimpl_->curl_ = curl_easy_init();
83 if (!pimpl_->curl_)
84 {
85 curl_slist_free_all(pimpl_->postHeaders_);
86 throw OrthancException(ErrorCode_NotEnoughMemory);
87 }
88
89 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEFUNCTION, &CurlCallback));
90 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HEADER, 0));
91 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_FOLLOWLOCATION, 1));
92
93 #if ORTHANC_SSL_ENABLED == 1
94 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_SSL_VERIFYPEER, 0));
95 #endif
96
97 // This fixes the "longjmp causes uninitialized stack frame" crash
98 // that happens on modern Linux versions.
99 // http://stackoverflow.com/questions/9191668/error-longjmp-causes-uninitialized-stack-frame
100 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOSIGNAL, 1));
101
102 url_ = "";
103 method_ = HttpMethod_Get;
104 lastStatus_ = HttpStatus_200_Ok;
105 isVerbose_ = false;
106 }
107
108
109 HttpClient::HttpClient() : pimpl_(new PImpl)
110 {
111 Setup();
112 }
113
114
115 HttpClient::HttpClient(const HttpClient& other) : pimpl_(new PImpl)
116 {
117 Setup();
118
119 if (other.IsVerbose())
120 {
121 SetVerbose(true);
122 }
123
124 if (other.credentials_.size() != 0)
125 {
126 credentials_ = other.credentials_;
127 }
128 }
129
130
131 HttpClient::~HttpClient()
132 {
133 curl_easy_cleanup(pimpl_->curl_);
134 curl_slist_free_all(pimpl_->postHeaders_);
135 }
136
137
138 void HttpClient::SetVerbose(bool isVerbose)
139 {
140 isVerbose_ = isVerbose;
141
142 if (isVerbose_)
143 {
144 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 1));
145 }
146 else
147 {
148 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_VERBOSE, 0));
149 }
150 }
151
152
153 bool HttpClient::Apply(std::string& answer)
154 {
155 answer.clear();
156 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_URL, url_.c_str()));
157 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_WRITEDATA, &answer));
158 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, NULL));
159
160 if (credentials_.size() != 0)
161 {
162 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_USERPWD, credentials_.c_str()));
163 }
164
165 switch (method_)
166 {
167 case HttpMethod_Get:
168 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPGET, 1L));
169 break;
170
171 case HttpMethod_Post:
172 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POST, 1L));
173 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_HTTPHEADER, pimpl_->postHeaders_));
174
175 if (postData_.size() > 0)
176 {
177 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, postData_.c_str()));
178 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, postData_.size()));
179 }
180 else
181 {
182 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDS, NULL));
183 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_POSTFIELDSIZE, 0));
184 }
185
186 break;
187
188 case HttpMethod_Delete:
189 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_NOBODY, 1L));
190 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_CUSTOMREQUEST, "DELETE"));
191 break;
192
193 case HttpMethod_Put:
194 CheckCode(curl_easy_setopt(pimpl_->curl_, CURLOPT_PUT, 1L));
195 break;
196
197 default:
198 throw OrthancException(ErrorCode_InternalError);
199 }
200
201 // Do the actual request
202 CheckCode(curl_easy_perform(pimpl_->curl_));
203
204 long status;
205 CheckCode(curl_easy_getinfo(pimpl_->curl_, CURLINFO_RESPONSE_CODE, &status));
206
207 if (status == 0)
208 {
209 // This corresponds to a call to an inexistent host
210 lastStatus_ = HttpStatus_500_InternalServerError;
211 }
212 else
213 {
214 lastStatus_ = static_cast<HttpStatus>(status);
215 }
216
217 return (status >= 200 && status < 300);
218 }
219
220
221 bool HttpClient::Apply(Json::Value& answer)
222 {
223 std::string s;
224 if (Apply(s))
225 {
226 Json::Reader reader;
227 return reader.parse(s, answer);
228 }
229 else
230 {
231 return false;
232 }
233 }
234
235
236 void HttpClient::SetCredentials(const char* username,
237 const char* password)
238 {
239 credentials_ = std::string(username) + ":" + std::string(password);
240 }
241
242
243 void HttpClient::GlobalInitialize()
244 {
245 CheckCode(curl_global_init(CURL_GLOBAL_DEFAULT));
246 }
247
248 void HttpClient::GlobalFinalize()
249 {
250 curl_global_cleanup();
251 }
252
253 const char* HttpClient::GetLastStatusText() const
254 {
255 return Toolbox::ToString(lastStatus_);
256 }
257 }