88#include " APIResponse.h"
99
1010namespace ConnectedSystemsAPI {
11+ struct RawHttpResponse {
12+ int responseCode = 0 ;
13+ std::string responseMessage;
14+ std::string responseBody;
15+ std::map<std::string, std::vector<std::string>> headers;
16+ };
17+
1118 class APIRequest {
1219 private:
1320 std::string apiRoot;
@@ -23,13 +30,13 @@ namespace ConnectedSystemsAPI {
2330 public:
2431 template <typename T>
2532 APIResponse<T> execute () {
26- std::string rawResponse = execute ();
27- return APIResponse<T>(0 , " " , rawResponse, {} );
33+ RawHttpResponse rawResponse = execute ();
34+ return APIResponse<T>(rawResponse. responseCode , rawResponse. responseMessage , rawResponse. responseBody , rawResponse. headers );
2835 }
2936
30- std::string execute () const {
37+ RawHttpResponse execute () const {
3138 CURL * curl = curl_easy_init ();
32- std::string response;
39+ RawHttpResponse response;
3340 if (curl) {
3441 std::string url = apiRoot + endpoint;
3542 struct curl_slist * header_list = nullptr ;
@@ -40,7 +47,10 @@ namespace ConnectedSystemsAPI {
4047 curl_easy_setopt (curl, CURLOPT_URL , url.c_str ());
4148 curl_easy_setopt (curl, CURLOPT_HTTPHEADER , header_list);
4249 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION , WriteCallback);
43- curl_easy_setopt (curl, CURLOPT_WRITEDATA , &response);
50+ curl_easy_setopt (curl, CURLOPT_WRITEDATA , &response.responseBody );
51+
52+ curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION , HeaderCallback);
53+ curl_easy_setopt (curl, CURLOPT_HEADERDATA , &response);
4454
4555 if (requestMethod == " POST" ) {
4656 curl_easy_setopt (curl, CURLOPT_POST , 1L );
@@ -58,6 +68,8 @@ namespace ConnectedSystemsAPI {
5868 if (res != CURLE_OK )
5969 std::cerr << " cURL error: " << curl_easy_strerror (res) << std::endl;
6070
71+ curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE , &response.responseCode );
72+
6173 curl_slist_free_all (header_list);
6274 curl_easy_cleanup (curl);
6375 }
@@ -71,6 +83,42 @@ namespace ConnectedSystemsAPI {
7183 return size * nmemb;
7284 }
7385
86+ static size_t HeaderCallback (char * buffer, size_t size, size_t nitems, void * userdata) {
87+ size_t totalSize = size * nitems;
88+ auto * response = static_cast <RawHttpResponse*>(userdata);
89+ std::string headerLine (buffer, totalSize);
90+
91+ // Check for the status line (e.g., "HTTP/1.1 200 OK\r\n")
92+ if (headerLine.find (" HTTP/" ) == 0 ) {
93+ // Remove trailing \r\n
94+ headerLine.erase (headerLine.find_last_not_of (" \r\n " ) + 1 );
95+ // Remove "HTTP/1.1 " prefix to get the status message
96+ size_t firstSpace = headerLine.find (' ' );
97+ if (firstSpace != std::string::npos) {
98+ size_t secondSpace = headerLine.find (' ' , firstSpace + 1 );
99+ if (secondSpace != std::string::npos) {
100+ response->responseMessage = headerLine.substr (secondSpace + 1 );
101+ }
102+ }
103+ else
104+ response->responseMessage = headerLine;
105+ }
106+ else {
107+ // Parse header: "Key: Value"
108+ auto colonPos = headerLine.find (' :' );
109+ if (colonPos != std::string::npos) {
110+ std::string key = headerLine.substr (0 , colonPos);
111+ std::string value = headerLine.substr (colonPos + 1 );
112+ // Trim whitespace and trailing \r\n
113+ key.erase (key.find_last_not_of (" \t\r\n " ) + 1 );
114+ value.erase (0 , value.find_first_not_of (" \t " ));
115+ value.erase (value.find_last_not_of (" \r\n " ) + 1 );
116+ response->headers [key].push_back (value);
117+ }
118+ }
119+ return totalSize;
120+ }
121+
74122 public:
75123 // Builder inner class
76124 class Builder {
0 commit comments