Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
http_transport.h
Go to the documentation of this file.
1#pragma once
2
3// BSD 3-Clause License
4// Copyright (c) 2025, 🍀☀🌕🌥 🌊
5// See the LICENSE file in the project root for full license information.
6
7
17#include "../core/error_codes.h"
18#include <string>
19#include <vector>
20#include <map>
21#include <unordered_map>
22#include <chrono>
23#include <memory>
24#include <functional>
25#include <iostream>
26
27namespace kcenon { namespace monitoring {
28
34 std::string url;
35 std::string method{"POST"};
36 std::unordered_map<std::string, std::string> headers;
37 std::vector<uint8_t> body;
38 std::chrono::milliseconds timeout{30000};
39 bool enable_compression{false};
40};
41
48 std::string status_message;
49 std::unordered_map<std::string, std::string> headers;
50 std::vector<uint8_t> body;
51 std::chrono::milliseconds elapsed{0};
52};
53
59public:
60 virtual ~http_transport() = default;
61
65 virtual common::Result<http_response> send(const http_request& request) = 0;
66
70 virtual bool is_available() const = 0;
71
75 virtual std::string name() const = 0;
76};
77
86private:
89
90public:
92
96 void set_response_handler(std::function<http_response(const http_request&)> handler) {
97 response_handler_ = std::move(handler);
98 }
99
106
107 common::Result<http_response> send(const http_request& request) override {
108 if (response_handler_) {
109 return common::ok(response_handler_(request));
110 }
111
112 http_response response;
113 if (simulate_success_) {
114 response.status_code = 202; // Accepted
115 response.status_message = "Accepted";
116 } else {
117 response.status_code = 503; // Service Unavailable
118 response.status_message = "Service Unavailable";
119 }
120 response.elapsed = std::chrono::milliseconds(10);
121
122 return common::ok(response);
123 }
124
125 bool is_available() const override {
126 return true;
127 }
128
129 std::string name() const override {
130 return "stub";
131 }
132};
133
142private:
143 std::chrono::milliseconds default_timeout_{30000};
144 bool use_tls_{false};
145
146public:
147 explicit simple_http_client(std::chrono::milliseconds timeout = std::chrono::milliseconds(30000))
148 : default_timeout_(timeout) {}
149
150 common::Result<http_response> send(const http_request& request) override {
151 // Parse URL to extract host, port, and path
152 auto url_parts = parse_url(request.url);
153 if (!url_parts.valid) {
155 "Invalid URL: " + request.url);
156 return common::Result<http_response>::err(err.to_common_error());
157 }
158
159 use_tls_ = url_parts.scheme == "https";
160
161 // No real HTTP transport available. Return error instead of
162 // fake 202 success, so callers know data was NOT sent.
163 // Build with MONITORING_WITH_NETWORK_SYSTEM=ON for real transport.
164 static bool warned = false;
165 if (!warned)
166 {
167 std::cerr << "[monitoring_system] WARNING: Trace export configured but no HTTP "
168 << "transport available. Build with MONITORING_WITH_NETWORK_SYSTEM=ON "
169 << "or provide a real http_transport implementation. "
170 << "Trace data is NOT being exported.\n";
171 warned = true;
172 }
173
175 "No HTTP transport available (stub mode)");
176 return common::Result<http_response>::err(err.to_common_error());
177 }
178
179 bool is_available() const override {
180 // Stub transport cannot actually send data
181 return false;
182 }
183
184 std::string name() const override {
185 return "stub";
186 }
187
188private:
189 struct url_parts {
190 std::string scheme;
191 std::string host;
192 int port{80};
193 std::string path;
194 bool valid{false};
195 };
196
197 url_parts parse_url(const std::string& url) {
198 url_parts parts;
199
200 // Find scheme
201 auto scheme_end = url.find("://");
202 if (scheme_end == std::string::npos) {
203 return parts;
204 }
205 parts.scheme = url.substr(0, scheme_end);
206
207 // Find host and port
208 auto host_start = scheme_end + 3;
209 auto path_start = url.find('/', host_start);
210 if (path_start == std::string::npos) {
211 path_start = url.length();
212 }
213
214 auto host_port = url.substr(host_start, path_start - host_start);
215 auto colon_pos = host_port.find(':');
216 if (colon_pos != std::string::npos) {
217 parts.host = host_port.substr(0, colon_pos);
218 try {
219 parts.port = std::stoi(host_port.substr(colon_pos + 1));
220 } catch (...) {
221 return parts;
222 }
223 } else {
224 parts.host = host_port;
225 parts.port = (parts.scheme == "https") ? 443 : 80;
226 }
227
228 // Extract path
229 if (path_start < url.length()) {
230 parts.path = url.substr(path_start);
231 } else {
232 parts.path = "/";
233 }
234
235 parts.valid = !parts.host.empty();
236 return parts;
237 }
238};
239
240#ifdef MONITORING_HAS_NETWORK_SYSTEM
241#include <kcenon/network/core/http_client.h>
242
250class network_http_transport : public http_transport {
251private:
252 std::shared_ptr<network_system::core::http_client> client_;
253
254public:
255 explicit network_http_transport(std::chrono::milliseconds timeout = std::chrono::milliseconds(30000))
256 : client_(std::make_shared<network_system::core::http_client>(timeout)) {}
257
258 common::Result<http_response> send(const http_request& request) override {
259 // Convert headers from unordered_map to map
260 std::map<std::string, std::string> headers(request.headers.begin(), request.headers.end());
261
262 // Make the actual HTTP request using network_system
263 network_system::core::Result<network_system::core::internal::http_response> net_result;
264
265 if (request.method == "GET") {
266 net_result = client_->get(request.url, {}, headers);
267 } else if (request.method == "POST") {
268 net_result = client_->post(request.url, request.body, headers);
269 } else if (request.method == "PUT") {
270 std::string body_str(request.body.begin(), request.body.end());
271 net_result = client_->put(request.url, body_str, headers);
272 } else if (request.method == "DELETE") {
273 net_result = client_->del(request.url, headers);
274 } else if (request.method == "HEAD") {
275 net_result = client_->head(request.url, headers);
276 } else if (request.method == "PATCH") {
277 std::string body_str(request.body.begin(), request.body.end());
278 net_result = client_->patch(request.url, body_str, headers);
279 } else {
280 return common::make_error<http_response>(static_cast<int>(monitoring_error_code::invalid_configuration),
281 "Unsupported HTTP method: " + request.method);
282 }
283
284 if (!net_result) {
285 return common::make_error<http_response>(static_cast<int>(monitoring_error_code::operation_failed),
286 "HTTP request failed: " + net_result.error().message);
287 }
288
289 // Convert network_system response to monitoring response
290 http_response response;
291 response.status_code = net_result->status_code;
292 response.status_message = net_result->status_text;
293 response.body = net_result->body;
294
295 // Convert headers
296 for (const auto& [key, value] : net_result->headers) {
297 response.headers[key] = value;
298 }
299
300 return common::ok(response);
301 }
302
303 bool is_available() const override {
304 return true;
305 }
306
307 std::string name() const override {
308 return "network_system";
309 }
310};
311#endif // MONITORING_HAS_NETWORK_SYSTEM
312
319inline std::unique_ptr<http_transport> create_default_transport() {
320#ifdef MONITORING_HAS_NETWORK_SYSTEM
321 return std::make_unique<network_http_transport>();
322#else
323 return std::make_unique<simple_http_client>();
324#endif
325}
326
330inline std::unique_ptr<stub_http_transport> create_stub_transport() {
331 return std::make_unique<stub_http_transport>();
332}
333
334#ifdef MONITORING_HAS_NETWORK_SYSTEM
338inline std::unique_ptr<network_http_transport> create_network_transport(
339 std::chrono::milliseconds timeout = std::chrono::milliseconds(30000)) {
340 return std::make_unique<network_http_transport>(timeout);
341}
342#endif
343
344} } // namespace kcenon::monitoring
Abstract HTTP transport interface.
virtual bool is_available() const =0
Check if transport is available.
virtual std::string name() const =0
Get transport name.
virtual common::Result< http_response > send(const http_request &request)=0
Send HTTP request and receive response.
Simple HTTP client using basic socket operations.
url_parts parse_url(const std::string &url)
simple_http_client(std::chrono::milliseconds timeout=std::chrono::milliseconds(30000))
common::Result< http_response > send(const http_request &request) override
Send HTTP request and receive response.
std::string name() const override
Get transport name.
std::chrono::milliseconds default_timeout_
bool is_available() const override
Check if transport is available.
Stub HTTP transport for testing.
void set_simulate_success(bool success)
Set whether to simulate success or failure.
void set_response_handler(std::function< http_response(const http_request &)> handler)
Set custom response handler for testing.
std::string name() const override
Get transport name.
common::Result< http_response > send(const http_request &request) override
Send HTTP request and receive response.
std::function< http_response(const http_request &)> response_handler_
bool is_available() const override
Check if transport is available.
Monitoring system specific error codes.
std::unique_ptr< http_transport > create_default_transport()
Create default HTTP transport.
std::unique_ptr< stub_http_transport > create_stub_transport()
Create stub HTTP transport for testing.
Result pattern type definitions for monitoring system.
Extended error information with context.
common::error_info to_common_error() const
Convert to common_system error_info.
HTTP request configuration.
std::chrono::milliseconds timeout
std::vector< uint8_t > body
std::unordered_map< std::string, std::string > headers
std::chrono::milliseconds elapsed
std::unordered_map< std::string, std::string > headers