libcyberradio 22.01.24
HttpsSession.cpp
1/***************************************************************************
2 * \file HttpsSession.cpp
3 * \brief Implements an interface for establishing an HTTPS session.
4 * \author DA
5 * \copyright (c) 2017 CyberRadio Solutions, Inc. All rights reserved.
6 *
7 * \note Requires C++11 compiler support.
8 *
9 ***************************************************************************/
10
11#include "LibCyberRadio/Common/HttpsSession.h"
12#include "LibCyberRadio/Common/Debuggable.h"
13#include "LibCyberRadio/Common/Pythonesque.h"
14#include <curl/curl.h>
15#include <memory>
16#include <cstring>
17
18
19namespace LibCyberRadio
20{
21
22 // Singleton-pattern object whose entire purpose is to call
23 // curl_global_init() once, when the first HttpsSession object
24 // is instantiated.
25 class CurlGlobalInitializer : public Debuggable
26 {
27 public:
28 CurlGlobalInitializer(CurlGlobalInitializer const&) = delete;
29 CurlGlobalInitializer& operator=(CurlGlobalInitializer const&) = delete;
30
31 static std::shared_ptr<CurlGlobalInitializer> instance()
32 {
33 static std::shared_ptr<CurlGlobalInitializer> s{new CurlGlobalInitializer};
34 return s;
35 }
36
37 private:
38 CurlGlobalInitializer() :
39 Debuggable(false, "CurlGlobalInitializer")
40 {
41 this->debug("Initializing CURL\n");
42 curl_global_init(CURL_GLOBAL_ALL);
43 }
44 };
45
46
48 bool debug
49 ) :
50 Debuggable(debug, "HttpsSession"),
51 _session(curl_easy_init()),
52 _responseCode(0),
53 //_postHeaders(NULL),
54 //_postBuffer(NULL),
55 _lastReqErrInfo("")
56 {
57 CurlGlobalInitializer::instance();
58 this->debug("CONSTRUCTED\n");
59 }
60
62 {
63 // Clear post header and data buffers
64 //clearPostBuffers();
65 // Clean up the session object
66 curl_easy_cleanup(_session);
67 this->debug("DESTROYED\n");
68 }
69
71 const std::string& url,
72 bool verify
73 )
74 {
75 this->debug("[get] Called; URL=%s, verify=%s\n",
76 url.c_str(), this->debugBool(verify));
77 CURLcode res;
78 // Clear HTTPS request stuff
79 res = initializeRequest();
80 // Set request options
81 this->debug("[get] Setting request options\n");
82 curl_easy_setopt(_session, CURLOPT_URL, url.c_str());
83 curl_easy_setopt(_session, CURLOPT_SSL_VERIFYPEER, verify ? 1L : 0L);
84 curl_easy_setopt(_session, CURLOPT_SSL_VERIFYHOST, verify ? 1L : 0L);
85 curl_easy_setopt(_session, CURLOPT_HTTPGET, 1L);
86 // Execute the request
87 this->debug("[get] Executing request\n");
88 res = curl_easy_perform(_session);
89 this->debug("[get] Request status: %d (%s)\n", res, this->debugCurl(res));
90 _lastReqErrInfo = this->debugCurl(res);
91 bool ret = (res == CURLE_OK);
92 if ( ret )
93 {
94 curl_easy_getinfo(_session, CURLINFO_RESPONSE_CODE, &_responseCode);
95 this->debug("[get] Response code: %ld\n", getResponseCode());
96 this->debug("[get] Header:\n%s\n", getResponseHeader().c_str());
97 this->debug("[get] Response:\n%s\n", getResponseBody().c_str());
98 // Handle HTTPS response codes that indicate error conditions
99 // The first line of the header will provide the reason why
100 if (_responseCode >= 400)
101 {
102 ret = false;
104 _lastReqErrInfo = vec[0];
105 }
106 }
107 this->debug("[get] Returning %s\n", this->debugBool(ret));
108 return ret;
109 }
110
112 const std::string& url,
113 void* data,
114 size_t length,
115 const char* contentType,
116 bool verify
117 )
118 {
119 this->debug("[post] Called; URL=%s, verify=%s\n",
120 url.c_str(), this->debugBool(verify));
121 CURLcode res;
122 // Clear HTTPS request stuff
124 // Set request options
125 this->debug("[post] Setting request options\n");
126 curl_easy_setopt(_session, CURLOPT_VERBOSE, 0L);
127 curl_easy_setopt(_session, CURLOPT_URL, url.c_str());
128 curl_easy_setopt(_session, CURLOPT_SSL_VERIFYPEER, verify ? 1L : 0L);
129 curl_easy_setopt(_session, CURLOPT_SSL_VERIFYHOST, verify ? 1L : 0L);
130 struct curl_slist* headers = NULL;
131 char ctheader[80];
132 memset(ctheader, 0, sizeof(ctheader));
133 strcpy(ctheader, "Content-type: ");
134 strcat(ctheader, contentType);
135 headers = curl_slist_append(headers, ctheader);
136 curl_easy_setopt(_session, CURLOPT_HTTPHEADER, headers);
137 curl_easy_setopt(_session, CURLOPT_POSTFIELDS, data);
138 curl_easy_setopt(_session, CURLOPT_POSTFIELDSIZE, (long)length);
139 curl_easy_setopt(_session, CURLOPT_POST, 1L);
140 // Execute the request
141 this->debug("[post] Executing request\n");
142 res = curl_easy_perform(_session);
143 this->debug("[post] Request status: %d (%s)\n", res, this->debugCurl(res));
144 _lastReqErrInfo = this->debugCurl(res);
145 bool ret = (res == CURLE_OK);
146 if ( ret )
147 {
148 curl_easy_getinfo(_session, CURLINFO_RESPONSE_CODE, &_responseCode);
149 this->debug("[post] Response code: %ld\n", getResponseCode());
150 this->debug("[post] Header:\n%s\n", getResponseHeader().c_str());
151 this->debug("[post] Response:\n%s\n", getResponseBody().c_str());
152 // Handle HTTPS response codes that indicate error conditions
153 // The first line of the header will provide the reason why
154 if (_responseCode >= 400)
155 {
156 ret = false;
158 _lastReqErrInfo = vec[0];
159 }
160 }
161 curl_slist_free_all(headers);
162 this->debug("[post] Returning %s\n", this->debugBool(ret));
163 return ret;
164 }
165
167 {
168 return _responseCode;
169 }
170
172 {
173 return _header.str();
174 }
175
177 {
178 return _response.str();
179 }
180
182 {
183 return _lastReqErrInfo;
184 }
185
187 char *ptr,
188 size_t size
189 )
190 {
191 //this->debug("[writeHeader] Called; size=%u\n", size);
192 _header.write(ptr, size);
193 //this->debug("[writeHeader] Returning\n");
194 return size;
195 }
196
198 char *ptr,
199 size_t size
200 )
201 {
202 //this->debug("[writeResponseData] Called; size=%u\n", size);
203 _response.write(ptr, size);
204 //this->debug("[writeResponseData] Returning\n");
205 return size;
206 }
207
209 {
210 this->debug("[initializeRequest] Called\n");
211 CURLcode res;
212 _header.clear();
213 _header.str("");
214 _response.clear();
215 _response.str("");
216 _responseCode = 0;
217 //clearPostBuffers();
218 curl_easy_reset(_session);
219 // Setting up the header buffer takes two steps:
220 // -- Set option CURLOPT_HEADERFUNCTION to the static
221 // headerCallback() method
222 // -- Set option CURLOPT_HEADERDATA to the "this" pointer
223 // for this object
224 res = curl_easy_setopt(_session, CURLOPT_HEADERFUNCTION,
226 if ( res == CURLE_OK )
227 {
228 res = curl_easy_setopt(_session, CURLOPT_HEADERDATA, this);
229 }
230 // Setting up the response buffer takes two steps:
231 // -- Set option CURLOPT_WRITEFUNCTION to the static
232 // writeDataCallback() method
233 // -- Set option CURLOPT_WRITEDATA to the "this" pointer
234 // for this object
235 if ( res == CURLE_OK )
236 {
237 res = curl_easy_setopt(_session, CURLOPT_WRITEFUNCTION,
239 }
240 if ( res == CURLE_OK )
241 {
242 res = curl_easy_setopt(_session, CURLOPT_WRITEDATA, this);
243 }
244 this->debug("[initializeRequest] Returning %d (%s)\n", res, this->debugCurl(res));
245 return res;
246 }
247
249 CURLcode x
250 )
251 {
252 return curl_easy_strerror(x);
253 }
254
256 char *buffer,
257 size_t size,
258 size_t nitems,
259 void *userdata
260 )
261 {
262 // "userdata" contains the "this" pointer to the session object
263 HttpsSession *session = (HttpsSession *)userdata;
264 size_t bytes = size * nitems;
265 return session->writeHeader(buffer, bytes);
266 }
267
269 char *ptr,
270 size_t size,
271 size_t nmemb,
272 void *userdata
273 )
274 {
275 // "userdata" contains the "this" pointer to the session object
276 HttpsSession *session = (HttpsSession *)userdata;
277 size_t bytes = size * nmemb;
278 return session->writeResponseData(ptr, bytes);
279 }
280
281} /* namespace LibCyberRadio */
Class that supports debug output.
Definition Debuggable.h:39
virtual const char * debugBool(bool x)
Gets a debug output string for a Boolean value.
virtual int debug(const char *format,...)
Outputs debug information.
Debuggable(bool debug=false, const std::string &debug_name="", FILE *debug_fp=DEBUG_FP, const std::string &debug_timefmt=DEBUG_TIME_FMT)
Constructs a Debuggable object.
static size_t writeDataCallback(char *ptr, size_t size, size_t nmemb, void *userdata)
Callback function that libcurl "writes" data to when retrieving an HTTPS response body.
virtual ~HttpsSession()
Destroys an HttpsSession object.
static size_t headerCallback(char *buffer, size_t size, size_t nitems, void *userdata)
Callback function that libcurl "writes" data to when retrieving an HTTPS response header.
virtual bool post(const std::string &url, void *data, size_t length, const char *contentType="text/plain", bool verify=true)
Posts data to a given URL over HTTPS.
virtual long getResponseCode() const
Gets the HTTPS response code from the last request.
virtual size_t writeResponseData(char *ptr, size_t size)
Write data into the response buffer.
virtual std::string getLastRequestErrorInfo() const
Gets the error information for the last request.
virtual bool get(const std::string &url, bool verify=true)
Gets data from a given URL over HTTPS.
virtual std::string getResponseBody() const
Gets the HTTPS response body from the last request.
virtual std::string getResponseHeader() const
Gets the HTTPS header from the last request.
virtual size_t writeHeader(char *ptr, size_t size)
Write data into the header buffer.
virtual CURLcode initializeRequest()
Initializes the session object to handle a new request.
HttpsSession(bool debug=false)
Constructs an HttpsSession object.
virtual const char * debugCurl(CURLcode x)
Returns a string corresponding to a libcurl error code.
static BasicStringList Split(const std::string &str, const std::string &sep, int maxsplit=INT_MAX)
Splits the given string into a list of string tokens.
Defines functionality for LibCyberRadio applications.
Definition App.h:24
BASIC_LIST_CONTAINER< std::string > BasicStringList
Type representing a list of strings.
Definition BasicList.h:25