1
1
#include " httpclient.hpp"
2
+ #include " file_utils.hpp"
3
+
4
+ #include < cassert>
5
+ #include < fstream>
6
+ #include < iostream>
7
+ #include < stdexcept>
8
+ #include < utility>
9
+ #include < vector>
10
+
11
+ class HttpClient ::HttpClientPrivate
12
+ {
13
+ public:
14
+ explicit HttpClientPrivate (HttpClient *q)
15
+ : q_ptr(q)
16
+ , curl(curl_easy_init())
17
+ {
18
+ if (curl == nullptr ) {
19
+ throw std::runtime_error (" curl_easy_init() failed" );
20
+ }
21
+
22
+ // setVerbose();
23
+ setHeaderCallback ();
24
+ }
25
+
26
+ void setVerbose ()
27
+ {
28
+ assert (curl != nullptr );
29
+ curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L );
30
+ }
31
+
32
+ void setHeaderCallback ()
33
+ {
34
+ assert (curl != nullptr );
35
+ curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, headerCallback);
36
+ curl_easy_setopt (curl, CURLOPT_HEADERDATA, q_ptr);
37
+ }
38
+
39
+ void setErrorCallback ()
40
+ {
41
+ assert (curl != nullptr );
42
+ curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, q_ptr->d_ptr ->errorBuffer .data ());
43
+ }
44
+
45
+ void setHeader (const Headers &headers)
46
+ {
47
+ assert (curl != nullptr );
48
+ for (const auto &[key, value] : std::as_const (headers)) {
49
+ this ->headers = curl_slist_append (this ->headers , (key + " : " + value).c_str ());
50
+ }
51
+ curl_easy_setopt (curl, CURLOPT_HTTPHEADER, this ->headers );
52
+ }
53
+
54
+ void clear ()
55
+ {
56
+ clearBuffer ();
57
+ curl_easy_cleanup (curl);
58
+ }
59
+
60
+ void clearBuffer ()
61
+ {
62
+ headerBuffer.clear ();
63
+ bodyBuffer.clear ();
64
+ errorBuffer.clear ();
65
+ curl_slist_free_all (headers);
66
+ headers = nullptr ;
67
+ }
68
+
69
+ void printError ()
70
+ {
71
+ if (!errorBuffer.empty ()) {
72
+ std::cerr << " Error: " << errorBuffer << std::endl;
73
+ }
74
+ }
75
+
76
+ static auto headerCallback (char *ptr, size_t size, size_t nmemb, void *userdata) -> size_t
77
+ {
78
+ auto *client = static_cast <HttpClient *>(userdata);
79
+ client->d_ptr ->headerBuffer .append (ptr, size * nmemb);
80
+ return size * nmemb;
81
+ }
82
+
83
+ static auto writeCallback (char *ptr, size_t size, size_t nmemb, void *userdata) -> size_t
84
+ {
85
+ auto *client = static_cast <HttpClient *>(userdata);
86
+ client->d_ptr ->bodyBuffer .append (ptr, size * nmemb);
87
+ return size * nmemb;
88
+ }
89
+
90
+ static auto downloadCallback (char *ptr, size_t size, size_t nmemb, void *userdata) -> size_t
91
+ {
92
+ auto *client = static_cast <HttpClient *>(userdata);
93
+ assert (client->d_ptr ->file .is_open ());
94
+ client->d_ptr ->file .write (ptr, size * nmemb);
95
+ return size * nmemb;
96
+ }
97
+
98
+ static auto downloadProgressCallback (void *clientp,
99
+ curl_off_t dltotal,
100
+ curl_off_t dlnow,
101
+ curl_off_t ultotal,
102
+ curl_off_t ulnow) -> int
103
+ {
104
+ std::cout << " Download progress: " << formatBytes (dlnow) << " /" << formatBytes (dltotal)
105
+ << std::endl;
106
+ return 0 ;
107
+ }
108
+
109
+ static auto uploadCallback (char *ptr, size_t size, size_t nmemb, void *userdata) -> size_t
110
+ {
111
+ auto *client = static_cast <HttpClient *>(userdata);
112
+ auto &file = client->d_ptr ->file ;
113
+ assert (file.is_open ());
114
+ file.read (ptr, size * nmemb);
115
+ return file.gcount ();
116
+ }
117
+
118
+ static auto uploadProgressCallback (void *clientp,
119
+ curl_off_t dltotal,
120
+ curl_off_t dlnow,
121
+ curl_off_t ultotal,
122
+ curl_off_t ulnow) -> int
123
+ {
124
+ std::cout << " Upload progress: " << formatBytes (ulnow) << " /" << formatBytes (ultotal)
125
+ << std::endl;
126
+ return 0 ;
127
+ }
128
+
129
+ HttpClient *q_ptr;
130
+
131
+ CURL *curl = nullptr ;
132
+ CURLcode res = CURLE_OK;
133
+ struct curl_slist *headers = nullptr ;
134
+ std::string headerBuffer = {};
135
+ std::string bodyBuffer = {};
136
+ std::string errorBuffer = {};
137
+ std::fstream file;
138
+ };
139
+
140
+ HttpClient::HttpClient ()
141
+ : d_ptr(std::make_unique<HttpClientPrivate>(this ))
142
+ {}
143
+
144
+ HttpClient::~HttpClient ()
145
+ {
146
+ d_ptr->clear ();
147
+ }
148
+
149
+ auto HttpClient::get (const std::string &url, const Headers &headers) -> std::string
150
+ {
151
+ return sendCustomRequest (url, " GET" , " " , headers);
152
+ }
153
+
154
+ auto HttpClient::post (const std::string &url, const std::string &data, const Headers &headers)
155
+ -> std::string
156
+ {
157
+ return sendCustomRequest (url, " POST" , data, headers);
158
+ }
159
+
160
+ auto HttpClient::put (const std::string &url, const std::string &data, const Headers &headers)
161
+ -> std::string
162
+ {
163
+ return sendCustomRequest (url, " PUT" , data, headers);
164
+ }
165
+
166
+ auto HttpClient::del (const std::string &url, const Headers &headers) -> std::string
167
+ {
168
+ return sendCustomRequest (url, " DELETE" , " " , headers);
169
+ }
170
+
171
+ auto HttpClient::options (const std::string &url, const Headers &headers) -> std::string
172
+ {
173
+ return sendCustomRequest (url, " OPTIONS" , " " , headers);
174
+ }
175
+
176
+ auto HttpClient::patch (const std::string &url, const std::string &data, const Headers &headers)
177
+ -> std::string
178
+ {
179
+ return sendCustomRequest (url, " PATCH" , data, headers);
180
+ }
2
181
3
182
auto HttpClient::sendCustomRequest (const std::string &url,
4
183
const std::string &method,
5
184
const std::string &data,
6
185
const Headers &headers) -> std::string
7
186
{
8
- m_buffer.clear ();
9
- m_headers = nullptr ;
187
+ d_ptr->clearBuffer ();
188
+
189
+ std::cout << " Sending (" << method << " ) request to (" << url << " )" << std::endl;
190
+
191
+ curl_easy_setopt (d_ptr->curl , CURLOPT_URL, url.c_str ());
192
+ curl_easy_setopt (d_ptr->curl , CURLOPT_CUSTOMREQUEST, method.c_str ());
193
+ curl_easy_setopt (d_ptr->curl , CURLOPT_POSTFIELDS, data.c_str ());
194
+ curl_easy_setopt (d_ptr->curl , CURLOPT_POSTFIELDSIZE, data.size ());
195
+ curl_easy_setopt (d_ptr->curl , CURLOPT_WRITEFUNCTION, HttpClientPrivate::writeCallback);
196
+ curl_easy_setopt (d_ptr->curl , CURLOPT_WRITEDATA, this );
197
+
198
+ d_ptr->setHeader (headers);
199
+
200
+ d_ptr->res = curl_easy_perform (d_ptr->curl );
201
+ if (d_ptr->res != CURLE_OK) {
202
+ throw std::runtime_error (curl_easy_strerror (d_ptr->res ));
203
+ }
204
+
205
+ d_ptr->printError ();
206
+
207
+ return d_ptr->bodyBuffer ;
208
+ }
209
+
210
+ auto HttpClient::download (const std::string &url,
211
+ const std::filesystem::path &path,
212
+ const Headers &headers) -> bool
213
+ {
214
+ d_ptr->clearBuffer ();
215
+
216
+ d_ptr->file .open (path, std::ios::out | std::ios::binary);
217
+ if (!d_ptr->file .is_open ()) {
218
+ d_ptr->bodyBuffer = " Failed to open file: " + path.string ();
219
+ return false ;
220
+ }
221
+
222
+ std::cout << " Downloading (" << url << " ) to (" << path << " )" << std::endl;
223
+
224
+ curl_easy_setopt (d_ptr->curl , CURLOPT_URL, url.c_str ());
225
+ curl_easy_setopt (d_ptr->curl , CURLOPT_CUSTOMREQUEST, " GET" );
226
+ curl_easy_setopt (d_ptr->curl , CURLOPT_WRITEFUNCTION, HttpClientPrivate::downloadCallback);
227
+ curl_easy_setopt (d_ptr->curl , CURLOPT_WRITEDATA, this );
228
+ curl_easy_setopt (d_ptr->curl , CURLOPT_NOPROGRESS, 0L );
229
+ curl_easy_setopt (d_ptr->curl ,
230
+ CURLOPT_XFERINFOFUNCTION,
231
+ HttpClientPrivate::downloadProgressCallback);
10
232
11
- curl_easy_setopt (m_curl, CURLOPT_URL, url.c_str ());
12
- curl_easy_setopt (m_curl, CURLOPT_CUSTOMREQUEST, method.c_str ());
13
- curl_easy_setopt (m_curl, CURLOPT_POSTFIELDS, data.c_str ());
14
- curl_easy_setopt (m_curl, CURLOPT_WRITEFUNCTION, writeCallback);
15
- curl_easy_setopt (m_curl, CURLOPT_WRITEDATA, this );
16
- curl_easy_setopt (m_curl, CURLOPT_HEADERFUNCTION, headerCallback);
17
- curl_easy_setopt (m_curl, CURLOPT_HEADERDATA, this );
233
+ d_ptr->setHeader (headers);
18
234
19
- for (const auto &[key, value] : headers) {
20
- m_headers = curl_slist_append (m_headers, (key + " : " + value).c_str ());
235
+ d_ptr->res = curl_easy_perform (d_ptr->curl );
236
+
237
+ d_ptr->file .close ();
238
+ if (d_ptr->res != CURLE_OK) {
239
+ throw std::runtime_error (curl_easy_strerror (d_ptr->res ));
21
240
}
22
- curl_easy_setopt (m_curl, CURLOPT_HTTPHEADER, m_headers);
23
241
24
- m_res = curl_easy_perform (m_curl);
25
- if (m_res != CURLE_OK) {
26
- throw std::runtime_error (curl_easy_strerror (m_res));
242
+ d_ptr->printError ();
243
+
244
+ return true ;
245
+ }
246
+
247
+ auto HttpClient::upload_put (const std::string &url,
248
+ const std::filesystem::path &path,
249
+ const Headers &headers) -> bool
250
+ {
251
+ d_ptr->clearBuffer ();
252
+
253
+ d_ptr->file .open (path, std::ios::in | std::ios::binary);
254
+ if (!d_ptr->file .is_open ()) {
255
+ std::cerr << " Failed to open file: " << path << std::endl;
256
+ return false ;
27
257
}
28
258
29
- return m_buffer;
30
- }
259
+ std::cout << " Uploading (" << path << " ) to (" << url << " )" << std::endl;
260
+
261
+ curl_easy_setopt (d_ptr->curl , CURLOPT_URL, url.c_str ());
262
+ curl_easy_setopt (d_ptr->curl , CURLOPT_CUSTOMREQUEST, " PUT" );
263
+ curl_easy_setopt (d_ptr->curl , CURLOPT_UPLOAD, 1L );
264
+ curl_easy_setopt (d_ptr->curl , CURLOPT_READFUNCTION, HttpClientPrivate::uploadCallback);
265
+ curl_easy_setopt (d_ptr->curl , CURLOPT_READDATA, this );
266
+ curl_easy_setopt (d_ptr->curl , CURLOPT_NOPROGRESS, 0L );
267
+ curl_easy_setopt (d_ptr->curl ,
268
+ CURLOPT_XFERINFOFUNCTION,
269
+ HttpClientPrivate::uploadProgressCallback);
270
+
271
+ d_ptr->setHeader (headers);
272
+
273
+ d_ptr->res = curl_easy_perform (d_ptr->curl );
274
+
275
+ d_ptr->file .close ();
276
+ if (d_ptr->res != CURLE_OK) {
277
+ throw std::runtime_error (curl_easy_strerror (d_ptr->res ));
278
+ }
279
+
280
+ d_ptr->printError ();
281
+
282
+ return true ;
283
+ }
284
+
285
+ auto HttpClient::upload_post (const std::string &url,
286
+ const std::filesystem::path &path,
287
+ const Headers &headers) -> bool
288
+ {
289
+ d_ptr->clearBuffer ();
290
+
291
+ std::cout << " Uploading (" << path << " ) to (" << url << " )" << std::endl;
292
+
293
+ curl_easy_setopt (d_ptr->curl , CURLOPT_URL, url.c_str ());
294
+ curl_easy_setopt (d_ptr->curl , CURLOPT_CUSTOMREQUEST, " POST" );
295
+ curl_easy_setopt (d_ptr->curl , CURLOPT_NOPROGRESS, 0L );
296
+ curl_easy_setopt (d_ptr->curl ,
297
+ CURLOPT_XFERINFOFUNCTION,
298
+ HttpClientPrivate::uploadProgressCallback);
299
+
300
+ d_ptr->setHeader (headers);
301
+
302
+ auto *form = curl_mime_init (d_ptr->curl );
303
+ auto *part = curl_mime_addpart (form);
304
+ curl_mime_name (part, " file" );
305
+ curl_mime_filedata (part, path.string ().c_str ());
306
+ curl_easy_setopt (d_ptr->curl , CURLOPT_MIMEPOST, form);
307
+
308
+ d_ptr->res = curl_easy_perform (d_ptr->curl );
309
+
310
+ if (d_ptr->res != CURLE_OK) {
311
+ throw std::runtime_error (curl_easy_strerror (d_ptr->res ));
312
+ }
313
+
314
+ d_ptr->printError ();
315
+
316
+ curl_mime_free (form);
317
+
318
+ return true ;
319
+ }
320
+
321
+ auto HttpClient::error () const -> std::string
322
+ {
323
+ return d_ptr->errorBuffer ;
324
+ }
0 commit comments