Skip to content

Commit 7084c02

Browse files
committed
first super rough draft of an implementation for the new API
1 parent c72a9e6 commit 7084c02

5 files changed

Lines changed: 410 additions & 568 deletions

File tree

include/restclient-cpp/connection.h

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,59 +43,90 @@ class Connection {
4343
*/
4444
typedef struct {
4545
std::string base_url;
46-
HeaderFields headers;
46+
RestClients::HeaderFields headers;
4747
int timeout;
4848
struct {
4949
std::string username;
5050
std::string password;
5151
} basicAuth;
5252
std::string customUserAgent;
53+
struct {
54+
// total time of the last request in seconds Total time of previous
55+
// transfer. See CURLINFO_TOTAL_TIME
56+
int totalTime;
57+
// time spent in DNS lookup in seconds Time from start until name
58+
// resolving completed. See CURLINFO_NAMELOOKUP_TIME
59+
int nameLookupTime;
60+
// time it took until Time from start until remote host or proxy
61+
// completed. See CURLINFO_CONNECT_TIME
62+
int connectTime;
63+
// Time from start until SSL/SSH handshake completed. See
64+
// CURLINFO_APPCONNECT_TIME
65+
int appConnectTime;
66+
// Time from start until just before the transfer begins. See
67+
// CURLINFO_PRETRANSFER_TIME
68+
int preTransferTime;
69+
// Time from start until just when the first byte is received. See
70+
// CURLINFO_STARTTRANSFER_TIME
71+
int startTransferTime;
72+
// Time taken for all redirect steps before the final transfer. See
73+
// CURLINFO_REDIRECT_TIME
74+
int redirectTime;
75+
// number of redirects followed. See CURLINFO_REDIRECT_COUNT
76+
int redirectCount;
77+
} lastRequest;
5378
} Info;
5479

5580

56-
explicit Connection(const std::string& baseUrl);
81+
explicit Connection(const std::string baseUrl);
5782
~Connection();
5883

5984
// Instance configuration methods
6085
// configure basic auth
6186
void SetBasicAuth(const std::string& username,
6287
const std::string& password);
6388

64-
// set connection timeout to 5s
89+
// set connection timeout to seconds
6590
void SetTimeout(int seconds);
6691

6792
// set custom user agent
6893
// (this will result in the UA "foo/cool restclient-cpp/VERSION")
6994
void SetUserAgent(const std::string& userAgent);
7095

96+
std::string GetUserAgent();
97+
7198
// set headers
72-
void SetHeaders(headermap headers);
99+
void SetHeaders(RestClient::HeaderFields headers);
100+
101+
// get headers
102+
RestClient::HeaderFields GetHeaders();
73103

74104
// append additional headers
75105
void AppendHeader(const std::string& key,
76106
const std::string& value);
77107

78108

79109
// Basic HTTP verb methods
80-
response get(const std::string& uri);
81-
response post(const std::string& uri,
82-
const std::string& contentType,
83-
const std::string& data);
84-
response put(const std::string& uri,
85-
const std::string& contentType,
86-
const std::string& data);
87-
response del(const std::string& uri);
110+
RestClient::Response get(const std::string& uri);
111+
RestClient::Response post(const std::string& uri,
112+
const std::string& data);
113+
RestClient::Response put(const std::string& uri,
114+
const std::string& data);
115+
RestCLient::Response del(const std::string& uri);
88116

89117
private:
90118
CURL* curlHandle = NULL;
91119
std::string baseUrl;
92-
headermap headers;
120+
RestClient::HeaderFields headerFields;
93121
int timeout;
94122
struct {
95123
std::string username;
96124
std::string password;
97125
} basicAuth;
98126
std::string customUserAgent;
127+
Info infoStruct;
128+
int performCurlRequest(const std::string& uri,
129+
RestClient::Response& ret);
99130
};
100131
}; // namespace RestClient
101132

source/connection.cc

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
/**
2+
* @file connection.cpp
3+
* @brief implementation of the connection class
4+
* @author Daniel Schauenberg <[email protected]>
5+
*/
6+
7+
#include "restclient-cpp/connection.h"
8+
9+
#include <curl/curl.h>
10+
11+
#include <cstring>
12+
#include <string>
13+
#include <iostream>
14+
#include <map>
15+
#include <stdexcept>
16+
17+
#include "restclient-cpp/restclient.h"
18+
#include "restclient-cpp/helpers.h"
19+
#include "restclient-cpp/version.h"
20+
21+
using namespace RestClient;
22+
23+
Connection::Connection(const std::string baseUrl) {
24+
this->curlHandle = curl_easy_init();
25+
if (!this->curlHandle) {
26+
throw std::runtime_error("Couldn't initialize curl handle");
27+
}
28+
this->baseUrl = baseUrl;
29+
this->infoStruct = new Info();
30+
}
31+
32+
Connection::~Connection() {
33+
if (this->curlHandle) {
34+
curl_easy_cleanup(this->curlHandle);
35+
}
36+
}
37+
38+
// getters/setters
39+
40+
41+
/**
42+
* @brief helper function to get called from the actual request methods to
43+
* prepare the curlHandle for transfer with generic options, perform the
44+
* request and record some stats from the last request and then reset the
45+
* handle with curl_easy_reset to its default state. This will keep things
46+
* like connections and session ID intact but makes sure you can change
47+
* parameters on the object for another request.
48+
*
49+
* @param uri URI to query
50+
* @param ret Reference to the Response struct that should be filled
51+
*
52+
* @return 0 on success and 1 on error
53+
*/
54+
int Connection::performCurlRequest(const std::string& uri,
55+
RestClient::Response& ret) {
56+
57+
std::string url = std::string(this->baseUrl + uri);
58+
std::string headerString;
59+
CURLcode res = CURLE_OK;
60+
curl_slist* headerList = NULL;
61+
62+
/** set query URL */
63+
curl_easy_setopt(this->curlHandle, CURLOPT_URL, url.c_str());
64+
/** set callback function */
65+
curl_easy_setopt(this->curlHandle, CURLOPT_WRITEFUNCTION, Helpers::write_callback);
66+
/** set data object to pass to callback function */
67+
curl_easy_setopt(this->curlHandle, CURLOPT_WRITEDATA, &ret);
68+
/** set the header callback function */
69+
curl_easy_setopt(this->curlHandle, CURLOPT_HEADERFUNCTION, Helpers::header_callback);
70+
/** callback object for headers */
71+
curl_easy_setopt(this->curlHandle, CURLOPT_HEADERDATA, &ret);
72+
/** set http headers */
73+
for (HeaderFields::const_iterator it = this->headerFields.begin();
74+
it != this->headerFields.end(); ++it) {
75+
headerString = it->first;
76+
headerString += ": ";
77+
headerString += it->second;
78+
headerList = curl_slist_append(headerList, headerString.c_str());
79+
}
80+
curl_easy_setopt(this->curlHandle, CURLOPT_HTTPHEADER, headerList);
81+
/** set user agent */
82+
curl_easy_setopt(this->curlHandle, CURLOPT_USERAGENT, this->GetUserAgent().c_str());
83+
84+
// set timeout
85+
if (this->timeout) {
86+
curl_easy_setopt(this->curlHandle, CURLOPT_TIMEOUT, this->timeout);
87+
// dont want to get a sig alarm on timeout
88+
curl_easy_setopt(this->curlHandle, CURLOPT_NOSIGNAL, 1);
89+
}
90+
res = curl_easy_perform(this->curlHandle);
91+
if (res != CURLE_OK) {
92+
if (res == CURLE_OPERATION_TIMEDOUT) {
93+
ret.code = res;
94+
ret.body = "Operation Timeout.";
95+
}
96+
97+
ret.body = "Failed to query.";
98+
ret.code = -1;
99+
}
100+
int64_t http_code = 0;
101+
curl_easy_getinfo(this->curlHandle, CURLINFO_RESPONSE_CODE, &http_code);
102+
ret.code = static_cast<int>(http_code);
103+
104+
// TODO: get metrics from curl handle
105+
// free header list
106+
curl_slist_free_all(headerList);
107+
// reset curl handle
108+
curl_easy_reset(this->curlHandle);
109+
return 0;
110+
}
111+
112+
/**
113+
* @brief HTTP GET method
114+
*
115+
* @param url to query
116+
*
117+
* @return response struct
118+
*/
119+
Response Connection::get(const std::string& url) {
120+
/** create return struct */
121+
Response ret = {};
122+
CURLcode res = CURLE_OK;
123+
124+
this->performCurlRequest(url, &ret);
125+
return ret;
126+
}
127+
/**
128+
* @brief HTTP POST method
129+
*
130+
* @param url to query
131+
* @param ctype content type as string
132+
* @param data HTTP POST body
133+
*
134+
* @return response struct
135+
*/
136+
Response Connection::post(const std::string& url,
137+
const std::string& data) {
138+
/** create return struct */
139+
RestClient::Response ret = {};
140+
141+
/** Now specify we want to POST data */
142+
curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L);
143+
/** set post fields */
144+
curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDS, data.c_str());
145+
curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDSIZE, data.size());
146+
147+
this->performCurlRequest(url, &ret);
148+
return ret;
149+
}
150+
/**
151+
* @brief HTTP PUT method
152+
*
153+
* @param url to query
154+
* @param ctype content type as string
155+
* @param data HTTP PUT body
156+
*
157+
* @return response struct
158+
*/
159+
RestClient::Response Connection::put(const std::string& url,
160+
const std::string& data) {
161+
/** create return struct */
162+
RestClient::Response ret = {};
163+
CURLcode res = CURLE_OK;
164+
165+
/** initialize upload object */
166+
RestClient::Helpers::UploadObject up_obj;
167+
up_obj.data = data.c_str();
168+
up_obj.length = data.size();
169+
170+
/** Now specify we want to PUT data */
171+
curl_easy_setopt(this->curlHandle, CURLOPT_PUT, 1L);
172+
curl_easy_setopt(this->curlHandle, CURLOPT_UPLOAD, 1L);
173+
/** set read callback function */
174+
curl_easy_setopt(this->curlHandle, CURLOPT_READFUNCTION,
175+
RestClient::Helpers::read_callback);
176+
/** set data object to pass to callback function */
177+
curl_easy_setopt(this->curlHandle, CURLOPT_READDATA, &up_obj);
178+
/** set data size */
179+
curl_easy_setopt(this->curlHandle, CURLOPT_INFILESIZE,
180+
static_cast<int64_t>(up_obj.length));
181+
182+
this->performCurlRequest(url, ret);
183+
return ret;
184+
}
185+
/**
186+
* @brief HTTP DELETE method
187+
*
188+
* @param url to query
189+
*
190+
* @return response struct
191+
*/
192+
RestClient::Response Connection::del(const std::string& url) {
193+
/** create return struct */
194+
RestClient::Response ret = {};
195+
CURLcode res = CURLE_OK;
196+
197+
/** we want HTTP DELETE */
198+
const char* http_delete = "DELETE";
199+
200+
/** set HTTP DELETE METHOD */
201+
curl_easy_setopt(this->curlHandle, CURLOPT_CUSTOMREQUEST, http_delete);
202+
203+
this->performCurlRequest(url, ret);
204+
return ret;
205+
}
206+

source/helpers.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @file helpers.cpp
3+
* @brief implementation of the restclient helpers
4+
* @author Daniel Schauenberg <[email protected]>
5+
*/
6+
7+
#include "restclient-cpp/helpers.h"
8+
#include "restclient-cpp/restclient.h"
9+
10+
/**
11+
* @brief write callback function for libcurl
12+
*
13+
* @param data returned data of size (size*nmemb)
14+
* @param size size parameter
15+
* @param nmemb memblock parameter
16+
* @param userdata pointer to user data to save/work with return data
17+
*
18+
* @return (size * nmemb)
19+
*/
20+
size_t RestClient::Helpers::write_callback(void *data, size_t size,
21+
size_t nmemb, void *userdata) {
22+
RestClient::Response* r;
23+
r = reinterpret_cast<RestClient::Response*>(userdata);
24+
r->body.append(reinterpret_cast<char*>(data), size*nmemb);
25+
26+
return (size * nmemb);
27+
}
28+
29+
/**
30+
* @brief header callback for libcurl
31+
*
32+
* @param data returned (header line)
33+
* @param size of data
34+
* @param nmemb memblock
35+
* @param userdata pointer to user data object to save headr data
36+
* @return size * nmemb;
37+
*/
38+
size_t RestClient::Helpers::header_callback(void *data, size_t size,
39+
size_t nmemb, void *userdata) {
40+
RestClient::Response* r;
41+
r = reinterpret_cast<RestClient::Response*>(userdata);
42+
std::string header(reinterpret_cast<char*>(data), size*nmemb);
43+
size_t seperator = header.find_first_of(":");
44+
if ( std::string::npos == seperator ) {
45+
// roll with non seperated headers...
46+
trim(header);
47+
if (0 == header.length()) {
48+
return (size * nmemb); // blank line;
49+
}
50+
r->headers[header] = "present";
51+
} else {
52+
std::string key = header.substr(0, seperator);
53+
trim(key);
54+
std::string value = header.substr(seperator + 1);
55+
trim(value);
56+
r->headers[key] = value;
57+
}
58+
59+
return (size * nmemb);
60+
}
61+
62+
/**
63+
* @brief read callback function for libcurl
64+
*
65+
* @param pointer of max size (size*nmemb) to write data to
66+
* @param size size parameter
67+
* @param nmemb memblock parameter
68+
* @param userdata pointer to user data to read data from
69+
*
70+
* @return (size * nmemb)
71+
*/
72+
size_t RestClient::Helpers::read_callback(void *data, size_t size,
73+
size_t nmemb, void *userdata) {
74+
/** get upload struct */
75+
RestClient::upload_object* u;
76+
u = reinterpret_cast<RestClient::upload_object*>(userdata);
77+
/** set correct sizes */
78+
size_t curl_size = size * nmemb;
79+
size_t copy_size = (u->length < curl_size) ? u->length : curl_size;
80+
/** copy data to buffer */
81+
memcpy(data, u->data, copy_size);
82+
/** decrement length and increment data pointer */
83+
u->length -= copy_size;
84+
u->data += copy_size;
85+
/** return copied size */
86+
return copy_size;
87+
}

0 commit comments

Comments
 (0)