Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
http_error.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6#include <sstream>
7#include <iomanip>
8#include <ctime>
9
11{
12
13auto get_error_status_text(http_error_code code) -> std::string_view
14{
15 switch (code)
16 {
17 // Client Errors (4xx)
19 return "Bad Request";
21 return "Unauthorized";
23 return "Payment Required";
25 return "Forbidden";
27 return "Not Found";
29 return "Method Not Allowed";
31 return "Not Acceptable";
33 return "Proxy Authentication Required";
35 return "Request Timeout";
37 return "Conflict";
39 return "Gone";
41 return "Length Required";
43 return "Precondition Failed";
45 return "Payload Too Large";
47 return "URI Too Long";
49 return "Unsupported Media Type";
51 return "Range Not Satisfiable";
53 return "Expectation Failed";
55 return "I'm a teapot";
57 return "Misdirected Request";
59 return "Unprocessable Entity";
61 return "Locked";
63 return "Failed Dependency";
65 return "Too Early";
67 return "Upgrade Required";
69 return "Precondition Required";
71 return "Too Many Requests";
73 return "Request Header Fields Too Large";
75 return "Unavailable For Legal Reasons";
76
77 // Server Errors (5xx)
79 return "Internal Server Error";
81 return "Not Implemented";
83 return "Bad Gateway";
85 return "Service Unavailable";
87 return "Gateway Timeout";
89 return "HTTP Version Not Supported";
91 return "Variant Also Negotiates";
93 return "Insufficient Storage";
95 return "Loop Detected";
97 return "Not Extended";
99 return "Network Authentication Required";
100 default:
101 return "Unknown Error";
102 }
103}
104
105// Helper to escape JSON strings
106static auto escape_json_string(const std::string& input) -> std::string
107{
108 std::ostringstream oss;
109 for (char c : input)
110 {
111 switch (c)
112 {
113 case '"':
114 oss << "\\\"";
115 break;
116 case '\\':
117 oss << "\\\\";
118 break;
119 case '\b':
120 oss << "\\b";
121 break;
122 case '\f':
123 oss << "\\f";
124 break;
125 case '\n':
126 oss << "\\n";
127 break;
128 case '\r':
129 oss << "\\r";
130 break;
131 case '\t':
132 oss << "\\t";
133 break;
134 default:
135 if (static_cast<unsigned char>(c) < 0x20)
136 {
137 oss << "\\u" << std::hex << std::setw(4) << std::setfill('0')
138 << static_cast<int>(c);
139 }
140 else
141 {
142 oss << c;
143 }
144 break;
145 }
146 }
147 return oss.str();
148}
149
150// Helper to escape HTML strings
151static auto escape_html_string(const std::string& input) -> std::string
152{
153 std::ostringstream oss;
154 for (char c : input)
155 {
156 switch (c)
157 {
158 case '&':
159 oss << "&amp;";
160 break;
161 case '<':
162 oss << "&lt;";
163 break;
164 case '>':
165 oss << "&gt;";
166 break;
167 case '"':
168 oss << "&quot;";
169 break;
170 case '\'':
171 oss << "&#39;";
172 break;
173 default:
174 oss << c;
175 break;
176 }
177 }
178 return oss.str();
179}
180
182{
183 http_response response;
184 response.status_code = error.status_code();
185 response.status_message = std::string(get_error_status_text(error.code));
186
187 // Build RFC 7807 compliant JSON response
188 std::ostringstream json;
189 json << "{\n";
190 json << " \"type\": \"about:blank\",\n";
191 json << " \"title\": \"" << escape_json_string(response.status_message) << "\",\n";
192 json << " \"status\": " << response.status_code << ",\n";
193 json << " \"detail\": \"" << escape_json_string(error.detail.empty() ? error.message : error.detail)
194 << "\"";
195
196 if (!error.request_id.empty())
197 {
198 json << ",\n \"instance\": \"" << escape_json_string(error.request_id) << "\"";
199 }
200
201 // Add timestamp
202 auto time_t_val = std::chrono::system_clock::to_time_t(error.timestamp);
203 std::tm tm_val{};
204#ifdef _WIN32
205 gmtime_s(&tm_val, &time_t_val);
206#else
207 gmtime_r(&time_t_val, &tm_val);
208#endif
209 char time_buf[32];
210 std::strftime(time_buf, sizeof(time_buf), "%Y-%m-%dT%H:%M:%SZ", &tm_val);
211 json << ",\n \"timestamp\": \"" << time_buf << "\"";
212
213 json << "\n}\n";
214
215 response.set_body_string(json.str());
216 response.set_header("Content-Type", "application/problem+json; charset=utf-8");
217
218 return response;
219}
220
222{
223 http_response response;
224 response.status_code = error.status_code();
225 response.status_message = std::string(get_error_status_text(error.code));
226
227 std::ostringstream html;
228 html << "<!DOCTYPE html>\n";
229 html << "<html lang=\"en\">\n";
230 html << "<head>\n";
231 html << " <meta charset=\"utf-8\">\n";
232 html << " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n";
233 html << " <title>" << response.status_code << " " << escape_html_string(response.status_message)
234 << "</title>\n";
235 html << " <style>\n";
236 html << " body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; "
237 << "margin: 40px auto; max-width: 650px; line-height: 1.6; color: #333; padding: 0 10px; }\n";
238 html << " h1 { color: #c00; border-bottom: 2px solid #eee; padding-bottom: 10px; }\n";
239 html << " .detail { background: #f9f9f9; padding: 15px; border-radius: 5px; }\n";
240 html << " .meta { color: #666; font-size: 0.9em; margin-top: 20px; }\n";
241 html << " </style>\n";
242 html << "</head>\n";
243 html << "<body>\n";
244 html << " <h1>" << response.status_code << " " << escape_html_string(response.status_message)
245 << "</h1>\n";
246
247 if (!error.message.empty())
248 {
249 html << " <p>" << escape_html_string(error.message) << "</p>\n";
250 }
251
252 if (!error.detail.empty())
253 {
254 html << " <div class=\"detail\">\n";
255 html << " <strong>Details:</strong> " << escape_html_string(error.detail) << "\n";
256 html << " </div>\n";
257 }
258
259 html << " <div class=\"meta\">\n";
260 if (!error.request_id.empty())
261 {
262 html << " <p>Request ID: " << escape_html_string(error.request_id) << "</p>\n";
263 }
264 html << " <p><em>NetworkSystem HTTP Server</em></p>\n";
265 html << " </div>\n";
266 html << "</body>\n";
267 html << "</html>\n";
268
269 response.set_body_string(html.str());
270 response.set_header("Content-Type", "text/html; charset=utf-8");
271
272 return response;
273}
274
275auto http_error_response::make_error(http_error_code code, const std::string& detail,
276 const std::string& request_id) -> http_error
277{
279 error.code = code;
280 error.message = std::string(get_error_status_text(code));
281 error.detail = detail;
282 error.request_id = request_id;
283 error.timestamp = std::chrono::system_clock::now();
284 return error;
285}
286
287} // namespace kcenon::network::internal
static auto build_json_error(const http_error &error) -> http_response
Build JSON format error response (RFC 7807)
static auto build_html_error(const http_error &error) -> http_response
Build HTML format error response.
static auto make_error(http_error_code code, const std::string &detail="", const std::string &request_id="") -> http_error
Create http_error from error code.
auto get_error_status_text(http_error_code code) -> std::string_view
Get status text for HTTP error code.
http_error_code
Standard HTTP error codes (RFC 7231)
Definition http_error.h:20
static auto escape_html_string(const std::string &input) -> std::string
static auto escape_json_string(const std::string &input) -> std::string
@ error
Black hole detected, reset to base.
Structured HTTP error information.
Definition http_error.h:78
Represents an HTTP response message.
Definition http_types.h:162
auto set_body_string(const std::string &content) -> void
Set body from string.
auto set_header(const std::string &name, const std::string &value) -> void
Set a header value.