yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
httplib_client.cc
Go to the documentation of this file.
2
3#include <regex>
4
5#include "util/macro.h" // For RETURN_IF_ERROR and ASSIGN_OR_RETURN
6
7// Include httplib with appropriate settings
8#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
9#define CPPHTTPLIB_OPENSSL_SUPPORT
10#endif
11#include "httplib.h"
12
13namespace yaze {
14namespace net {
15
17 // Constructor
18}
19
21 // Cleanup cached clients
22 client_cache_.clear();
23}
24
25absl::Status HttpLibClient::ParseUrl(const std::string& url,
26 std::string& scheme,
27 std::string& host,
28 int& port,
29 std::string& path) const {
30 // Basic URL regex pattern
31 std::regex url_regex(R"(^(https?):\/\/([^:\/\s]+)(?::(\d+))?(\/.*)?$)");
32 std::smatch matches;
33
34 if (!std::regex_match(url, matches, url_regex)) {
35 return absl::InvalidArgumentError("Invalid URL format: " + url);
36 }
37
38 scheme = matches[1].str();
39 host = matches[2].str();
40
41 // Parse port or use defaults
42 if (matches[3].matched) {
43 port = std::stoi(matches[3].str());
44 } else {
45 port = (scheme == "https") ? 443 : 80;
46 }
47
48 // Parse path (default to "/" if empty)
49 path = matches[4].matched ? matches[4].str() : "/";
50
51 return absl::OkStatus();
52}
53
54absl::StatusOr<std::shared_ptr<httplib::Client>> HttpLibClient::GetOrCreateClient(
55 const std::string& scheme,
56 const std::string& host,
57 int port) {
58
59 // Create cache key
60 std::string cache_key = scheme + "://" + host + ":" + std::to_string(port);
61
62 // Check if client exists in cache
63 auto it = client_cache_.find(cache_key);
64 if (it != client_cache_.end() && it->second) {
65 return it->second;
66 }
67
68 // Create new client
69 std::shared_ptr<httplib::Client> client;
70
71 if (scheme == "https") {
72#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
73 client = std::make_shared<httplib::Client>(host, port);
74 client->enable_server_certificate_verification(false); // For development
75#else
76 return absl::UnimplementedError(
77 "HTTPS not supported: OpenSSL support not compiled in");
78#endif
79 } else if (scheme == "http") {
80 client = std::make_shared<httplib::Client>(host, port);
81 } else {
82 return absl::InvalidArgumentError("Unsupported URL scheme: " + scheme);
83 }
84
85 if (!client) {
86 return absl::InternalError("Failed to create HTTP client");
87 }
88
89 // Set timeout from base class
90 client->set_connection_timeout(timeout_seconds_);
91 client->set_read_timeout(timeout_seconds_);
92 client->set_write_timeout(timeout_seconds_);
93
94 // Cache the client
95 client_cache_[cache_key] = client;
96
97 return client;
98}
99
100Headers HttpLibClient::ConvertHeaders(const void* httplib_headers) const {
101 Headers result;
102
103 if (httplib_headers) {
104 const auto& headers = *static_cast<const httplib::Headers*>(httplib_headers);
105 for (const auto& header : headers) {
106 result[header.first] = header.second;
107 }
108 }
109
110 return result;
111}
112
113absl::StatusOr<HttpResponse> HttpLibClient::Get(
114 const std::string& url,
115 const Headers& headers) {
116
117 std::string scheme, host, path;
118 int port;
119
120 RETURN_IF_ERROR(ParseUrl(url, scheme, host, port, path));
121
122 auto client_or = GetOrCreateClient(scheme, host, port);
123 ASSIGN_OR_RETURN(auto client, client_or);
124
125 // Convert headers to httplib format
126 httplib::Headers httplib_headers;
127 for (const auto& [key, value] : headers) {
128 httplib_headers.emplace(key, value);
129 }
130
131 // Perform GET request
132 auto res = client->Get(path.c_str(), httplib_headers);
133
134 if (!res) {
135 return absl::UnavailableError("HTTP GET request failed: " + url);
136 }
137
138 HttpResponse response;
139 response.status_code = res->status;
140 response.body = res->body;
141 response.headers = ConvertHeaders(&res->headers);
142
143 return response;
144}
145
146absl::StatusOr<HttpResponse> HttpLibClient::Post(
147 const std::string& url,
148 const std::string& body,
149 const Headers& headers) {
150
151 std::string scheme, host, path;
152 int port;
153
154 RETURN_IF_ERROR(ParseUrl(url, scheme, host, port, path));
155
156 auto client_or = GetOrCreateClient(scheme, host, port);
157 ASSIGN_OR_RETURN(auto client, client_or);
158
159 // Convert headers to httplib format
160 httplib::Headers httplib_headers;
161 for (const auto& [key, value] : headers) {
162 httplib_headers.emplace(key, value);
163 }
164
165 // Set Content-Type if not provided
166 if (httplib_headers.find("Content-Type") == httplib_headers.end()) {
167 httplib_headers.emplace("Content-Type", "application/json");
168 }
169
170 // Perform POST request
171 auto res = client->Post(path.c_str(), httplib_headers, body,
172 httplib_headers.find("Content-Type")->second);
173
174 if (!res) {
175 return absl::UnavailableError("HTTP POST request failed: " + url);
176 }
177
178 HttpResponse response;
179 response.status_code = res->status;
180 response.body = res->body;
181 response.headers = ConvertHeaders(&res->headers);
182
183 return response;
184}
185
186absl::StatusOr<HttpResponse> HttpLibClient::Put(
187 const std::string& url,
188 const std::string& body,
189 const Headers& headers) {
190
191 std::string scheme, host, path;
192 int port;
193
194 RETURN_IF_ERROR(ParseUrl(url, scheme, host, port, path));
195
196 auto client_or = GetOrCreateClient(scheme, host, port);
197 ASSIGN_OR_RETURN(auto client, client_or);
198
199 // Convert headers to httplib format
200 httplib::Headers httplib_headers;
201 for (const auto& [key, value] : headers) {
202 httplib_headers.emplace(key, value);
203 }
204
205 // Set Content-Type if not provided
206 if (httplib_headers.find("Content-Type") == httplib_headers.end()) {
207 httplib_headers.emplace("Content-Type", "application/json");
208 }
209
210 // Perform PUT request
211 auto res = client->Put(path.c_str(), httplib_headers, body,
212 httplib_headers.find("Content-Type")->second);
213
214 if (!res) {
215 return absl::UnavailableError("HTTP PUT request failed: " + url);
216 }
217
218 HttpResponse response;
219 response.status_code = res->status;
220 response.body = res->body;
221 response.headers = ConvertHeaders(&res->headers);
222
223 return response;
224}
225
226absl::StatusOr<HttpResponse> HttpLibClient::Delete(
227 const std::string& url,
228 const Headers& headers) {
229
230 std::string scheme, host, path;
231 int port;
232
233 RETURN_IF_ERROR(ParseUrl(url, scheme, host, port, path));
234
235 auto client_or = GetOrCreateClient(scheme, host, port);
236 ASSIGN_OR_RETURN(auto client, client_or);
237
238 // Convert headers to httplib format
239 httplib::Headers httplib_headers;
240 for (const auto& [key, value] : headers) {
241 httplib_headers.emplace(key, value);
242 }
243
244 // Perform DELETE request
245 auto res = client->Delete(path.c_str(), httplib_headers);
246
247 if (!res) {
248 return absl::UnavailableError("HTTP DELETE request failed: " + url);
249 }
250
251 HttpResponse response;
252 response.status_code = res->status;
253 response.body = res->body;
254 response.headers = ConvertHeaders(&res->headers);
255
256 return response;
257}
258
259void HttpLibClient::SetTimeout(int timeout_seconds) {
260 IHttpClient::SetTimeout(timeout_seconds);
261
262 // Clear client cache to force recreation with new timeout
263 client_cache_.clear();
264}
265
266} // namespace net
267} // namespace yaze
absl::StatusOr< HttpResponse > Get(const std::string &url, const Headers &headers={}) override
Perform an HTTP GET request.
absl::StatusOr< std::shared_ptr< httplib::Client > > GetOrCreateClient(const std::string &scheme, const std::string &host, int port)
Create or get cached httplib client for a host.
void SetTimeout(int timeout_seconds) override
Set a timeout for HTTP requests.
Headers ConvertHeaders(const void *httplib_headers) const
Convert httplib headers to our Headers type.
absl::StatusOr< HttpResponse > Post(const std::string &url, const std::string &body, const Headers &headers={}) override
Perform an HTTP POST request.
absl::Status ParseUrl(const std::string &url, std::string &scheme, std::string &host, int &port, std::string &path) const
Parse URL into components.
absl::StatusOr< HttpResponse > Delete(const std::string &url, const Headers &headers={}) override
Perform an HTTP DELETE request.
std::map< std::string, std::shared_ptr< httplib::Client > > client_cache_
absl::StatusOr< HttpResponse > Put(const std::string &url, const std::string &body, const Headers &headers={}) override
Perform an HTTP PUT request.
virtual void SetTimeout(int timeout_seconds)
Set a timeout for HTTP requests.
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:62
std::map< std::string, std::string > Headers
HTTP headers type definition.
Definition http_client.h:16
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
HTTP response structure containing status, body, and headers.
Definition http_client.h:22