ndmspc  v1.2.0-0.1.rc3
NHttpRequest.cxx
1 #include <fstream>
2 #include <iostream>
3 #include <sstream>
4 #include <vector>
5 #include <TString.h>
6 #include <TBase64.h>
7 #include <curl/curl.h>
8 #include "NLogger.h"
9 #include "NHttpRequest.h"
10 
11 namespace Ndmspc {
12 NHttpRequest::NHttpRequest() : curl(nullptr), headers(nullptr)
13 {
14  curl_global_init(CURL_GLOBAL_DEFAULT);
15  curl = curl_easy_init();
16  if (!curl) {
17  throw std::runtime_error("Error: curl_easy_init() failed");
18  }
19 }
20 
22 {
23  if (curl) {
24  curl_easy_cleanup(curl);
25  }
26  if (headers) {
27  curl_slist_free_all(headers);
28  }
29  curl_global_cleanup();
30 }
31 
32 std::string Ndmspc::NHttpRequest::get(const std::string & url, const std::string & cert_path,
33  const std::string & key_path, const std::string & key_password_file,
34  bool insecure)
35 {
36  std::ostringstream response_stream;
37  CURLcode res = request("GET", url, "", response_stream, cert_path, key_path, key_password_file, insecure);
38  if (res != CURLE_OK) {
39  throw_curl_error(res);
40  }
41  return response_stream.str();
42 }
43 
44 std::string Ndmspc::NHttpRequest::post(const std::string & url, const std::string & post_data,
45  const std::string & cert_path, const std::string & key_path,
46  const std::string & key_password_file, bool insecure)
47 {
48  std::ostringstream response_stream;
49  CURLcode res = request("POST", url, post_data, response_stream, cert_path, key_path, key_password_file, insecure);
50  if (res != CURLE_OK) {
51  throw_curl_error(res);
52  }
53  return response_stream.str();
54 }
55 
56 int Ndmspc::NHttpRequest::head(const std::string & url, const std::string & cert_path, const std::string & key_path,
57  const std::string & key_password_file, bool insecure)
58 {
59  std::ostringstream response_stream;
60  CURLcode res = request("HEAD", url, "", response_stream, cert_path, key_path, key_password_file, insecure);
61  if (res != CURLE_OK) {
62  throw_curl_error(res);
63  }
64  // Find HTTP in header to get the response code
65  int http_code = -1;
66  for (const auto & header : received_headers) {
67  if (header.find("HTTP/") != std::string::npos) {
68  sscanf(header.c_str(), "HTTP/%*s %d", &http_code);
69  break;
70  }
71  }
72 
73  return http_code;
74 }
75 
76 CURLcode Ndmspc::NHttpRequest::request(const std::string & method, const std::string & url, const std::string & data,
77  std::ostringstream & response_stream, const std::string & cert_path,
78  const std::string & key_path, const std::string & key_password_file,
79  bool insecure)
80 {
81  CURLcode res = CURLE_OK;
82 
83  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
84 
85  received_headers.clear();
86  // Set method (GET or POST)
87  if (method == "POST") {
88  curl_easy_setopt(curl, CURLOPT_POST, 1L);
89  curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
90  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
91  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_stream);
92  // Set Content-Type header to application/json
93  headers = curl_slist_append(headers, "Content-Type: application/json");
94  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
95  }
96  else if (method == "HEAD") {
97  // curl_easy_setopt(curl, CURLOPT_HTTPHEADER, 1L);
98  curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); // Set the header callback function
99  curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);
100  // Pass our vector address to the callback function via userdata
101  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
102  curl_easy_setopt(curl, CURLOPT_HEADERDATA, &received_headers);
103  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
104  curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); // 10 seconds
105  }
106  else if (method == "GET") {
107  curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
108  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
109  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_stream);
110  // Set Content-Type header to application/json
111  headers = curl_slist_append(headers, "Content-Type: application/json");
112  curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
113  }
114  else {
115  return CURLE_UNSUPPORTED_PROTOCOL; // Unsupported method
116  }
117 
118  // SSL certificate and key
119  if (!cert_path.empty() && !key_path.empty()) {
120  curl_easy_setopt(curl, CURLOPT_SSLCERT, cert_path.c_str());
121  curl_easy_setopt(curl, CURLOPT_SSLKEY, key_path.c_str());
122 
123  // Set the key password file
124  if (!key_password_file.empty()) {
125  curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, NULL);
126  curl_easy_setopt(curl, CURLOPT_KEYPASSWD, NULL);
127  curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, "");
128  curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "");
129 
130  std::ifstream passwordFile(key_password_file);
131  std::string encodedPassword;
132  if (passwordFile.is_open()) {
133  std::getline(passwordFile, encodedPassword);
134  passwordFile.close();
135 
136  TString encoded(encodedPassword); // Use TString
137  TString decoded = TBase64::Decode(encoded); // Decode with TBase64
138  std::string password = decoded.Data(); // Convert TString back to std::string
139 
140  // Remove carriage return if it exists, to avoid authentication failure.
141  if (!password.empty() && password.back() == '\r') {
142  password.pop_back();
143  }
144 
145  curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, password.c_str());
146  curl_easy_setopt(curl, CURLOPT_KEYPASSWD, password.c_str());
147  }
148  else {
149  std::cerr << "Error: Could not open password file: " << key_password_file << std::endl;
150  return CURLE_FILE_COULDNT_READ_FILE;
151  }
152  }
153  }
154 
155  // Insecure option
156  if (insecure) {
157  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
158  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
159  }
160  else {
161  // Add the CA cert path here for secure connection
162  // curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/ca/cert.pem");
163  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
164  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
165  }
166 
167  res = curl_easy_perform(curl);
168  if (res != CURLE_OK) {
169  return res; // Return the error code
170  }
171 
172  // // process received headers for HEAD request
173  // for (const auto & header : received_headers) {
174  // NLogInfo("Header: %s", header.c_str());
175  // }
176 
177  return res;
178 }
179 
180 size_t Ndmspc::NHttpRequest::WriteCallback(void * contents, size_t size, size_t nmemb, void * userp)
181 {
182  (static_cast<std::ostringstream *>(userp))->write(static_cast<char *>(contents), size * nmemb);
183  return size * nmemb;
184 }
185 
186 size_t Ndmspc::NHttpRequest::HeaderCallback(char * buffer, size_t size, size_t nitems, void * userdata)
187 {
188  size_t len = size * nitems;
189  std::string header(buffer, len);
190 
191  // Remove trailing newlines and carriage returns
192  if (!header.empty() && header.back() == '\n') {
193  header.pop_back();
194  }
195  if (!header.empty() && header.back() == '\r') {
196  header.pop_back();
197  }
198 
199  // Cast userdata back to the vector we passed
200  std::vector<std::string> * headers = static_cast<std::vector<std::string> *>(userdata);
201  if (headers) {
202  headers->push_back(header);
203  }
204 
205  return len; // Must return the number of bytes processed
206 }
207 
209 {
210  throw std::runtime_error(std::string("curl error: ") + curl_easy_strerror(res));
211 }
212 } // namespace Ndmspc
std::string post(const std::string &url, const std::string &post_data, const std::string &cert_path="", const std::string &key_path="", const std::string &key_password_file="", bool insecure=false)
Performs an HTTP POST request.
virtual ~NHttpRequest()
Destroys the NHttpRequest instance.
std::string get(const std::string &url, const std::string &cert_path="", const std::string &key_path="", const std::string &key_password_file="", bool insecure=false)
Performs an HTTP GET request.
static size_t HeaderCallback(char *buffer, size_t size, size_t nitems, void *userdata)
Callback for handling response headers.
int head(const std::string &url, const std::string &cert_path="", const std::string &key_path="", const std::string &key_password_file="", bool insecure=false)
Performs an HTTP HEAD request.
static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
Callback for writing response data.
NHttpRequest()
Constructs a new NHttpRequest instance.
CURL * curl
libcurl handle
Definition: NHttpRequest.h:108
CURLcode request(const std::string &method, const std::string &url, const std::string &data, std::ostringstream &response, const std::string &cert_path, const std::string &key_path, const std::string &key_password_file, bool insecure)
Internal method to perform an HTTP request.
void throw_curl_error(CURLcode res) const
Throws an exception if a CURL error occurs.