Pharos 0.7.23
Modern Web Framework & Web App Hosting Service.
Loading...
Searching...
No Matches
native_http_log.c
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Supplies MIME lookup, status parsing, client IP extraction, and log-safe HTTP formatting helpers.
4 */
5
6#include "native_http_log.h"
7
8#include "native_support.h"
9
10#include <ctype.h>
11#include <stdlib.h>
12#include <string.h>
13#ifndef _WIN32
14#include <strings.h>
15#endif
16
17#ifdef _WIN32
18#define pharos_http_log_strdup _strdup
19#define pharos_http_log_strcasecmp _stricmp
20#else
21#define pharos_http_log_strdup strdup
22#define pharos_http_log_strcasecmp strcasecmp
23#endif
24
25char *mime_type_for_path(const char *path) {
26 const char *ext = strrchr(path, '.');
27 if (ext == NULL) {
28 return pharos_http_log_strdup("application/octet-stream");
29 }
30 if (strcmp(ext, ".html") == 0) return pharos_http_log_strdup("text/html; charset=utf-8");
31 if (strcmp(ext, ".json") == 0) return pharos_http_log_strdup("application/json");
32 if (strcmp(ext, ".css") == 0) return pharos_http_log_strdup("text/css; charset=utf-8");
33 if (strcmp(ext, ".js") == 0) return pharos_http_log_strdup("application/javascript; charset=utf-8");
34 if (strcmp(ext, ".svg") == 0) return pharos_http_log_strdup("image/svg+xml");
35 if (strcmp(ext, ".png") == 0) return pharos_http_log_strdup("image/png");
36 if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) return pharos_http_log_strdup("image/jpeg");
37 return pharos_http_log_strdup("application/octet-stream");
38}
39
40char *compact_log_text_native(const char *value) {
41 Buffer buffer;
42 const unsigned char *cursor;
43 int pending_space = 0;
44 buffer_init(&buffer);
45 if (value == NULL) {
46 return pharos_http_log_strdup("");
47 }
48 for (cursor = (const unsigned char *)value; *cursor != '\0'; cursor++) {
49 if (*cursor == '\r' || *cursor == '\n' || isspace(*cursor)) {
50 pending_space = 1;
51 continue;
52 }
53 if (pending_space && buffer.len > 0) {
54 if (buffer_append(&buffer, " ", 1) != 0) {
55 buffer_free(&buffer);
56 return NULL;
57 }
58 pending_space = 0;
59 }
60 {
61 char single = (char)*cursor;
62 if (buffer_append(&buffer, &single, 1) != 0) {
63 buffer_free(&buffer);
64 return NULL;
65 }
66 }
67 }
68 if (buffer.len > 0 && buffer.data[buffer.len - 1] == ' ') {
69 buffer.data[buffer.len - 1] = '\0';
70 buffer.len--;
71 }
72 return buffer.data != NULL ? buffer.data : pharos_http_log_strdup("");
73}
74
75char *request_real_ip_native(const char *headers) {
76 char *copy = pharos_http_log_strdup(headers != NULL ? headers : "");
77 char *cursor;
78 if (copy == NULL) {
79 return pharos_http_log_strdup("unknown");
80 }
81 cursor = copy;
82 while (cursor != NULL && *cursor != '\0') {
83 char *line_end = strchr(cursor, '\n');
84 char *line = cursor;
85 char *colon;
86 if (line_end != NULL) {
87 *line_end = '\0';
88 }
89 trim_in_place(line);
90 colon = strchr(line, ':');
91 if (colon != NULL) {
92 char *value;
93 *colon = '\0';
94 value = colon + 1;
95 trim_in_place(line);
96 trim_in_place(value);
97 if (pharos_http_log_strcasecmp(line, "X-Forwarded-For") == 0) {
98 char *comma = strchr(value, ',');
99 if (comma != NULL) {
100 *comma = '\0';
101 trim_in_place(value);
102 }
103 free(copy);
104 return pharos_http_log_strdup(value[0] != '\0' ? value : "unknown");
105 }
106 }
107 if (line_end == NULL) {
108 break;
109 }
110 cursor = line_end + 1;
111 }
112 free(copy);
113 copy = pharos_http_log_strdup(headers != NULL ? headers : "");
114 if (copy == NULL) {
115 return pharos_http_log_strdup("unknown");
116 }
117 cursor = copy;
118 while (cursor != NULL && *cursor != '\0') {
119 char *line_end = strchr(cursor, '\n');
120 char *line = cursor;
121 char *colon;
122 if (line_end != NULL) {
123 *line_end = '\0';
124 }
125 trim_in_place(line);
126 colon = strchr(line, ':');
127 if (colon != NULL) {
128 char *value;
129 *colon = '\0';
130 value = colon + 1;
131 trim_in_place(line);
132 trim_in_place(value);
133 if (pharos_http_log_strcasecmp(line, "X-Real-IP") == 0) {
134 free(copy);
135 return pharos_http_log_strdup(value[0] != '\0' ? value : "unknown");
136 }
137 }
138 if (line_end == NULL) {
139 break;
140 }
141 cursor = line_end + 1;
142 }
143 free(copy);
144 return pharos_http_log_strdup("unknown");
145}
146
147char *request_headers_with_client_ip_native(const char *headers, const char *client_ip) {
148 Buffer buffer;
149 char *copy;
150 char *cursor;
151 int replaced = 0;
152 buffer_init(&buffer);
153 copy = pharos_http_log_strdup(headers != NULL ? headers : "");
154 if (copy == NULL) {
155 return NULL;
156 }
157 cursor = copy;
158 while (cursor != NULL && *cursor != '\0') {
159 char *line_end = strchr(cursor, '\n');
160 char *line = cursor;
161 char *colon;
162 if (line_end != NULL) {
163 *line_end = '\0';
164 }
165 trim_in_place(line);
166 colon = strchr(line, ':');
167 if (colon != NULL) {
168 *colon = '\0';
169 trim_in_place(line);
170 trim_in_place(colon + 1);
171 if (pharos_http_log_strcasecmp(line, "X-Real-IP") == 0) {
172 if (buffer_append(&buffer, "X-Real-IP:", 10) != 0 ||
173 buffer_append(&buffer, client_ip != NULL ? client_ip : "unknown", strlen(client_ip != NULL ? client_ip : "unknown")) != 0 ||
174 buffer_append(&buffer, "\n", 1) != 0) {
175 buffer_free(&buffer);
176 free(copy);
177 return NULL;
178 }
179 replaced = 1;
180 } else {
181 if (buffer_append(&buffer, line, strlen(line)) != 0 ||
182 buffer_append(&buffer, ":", 1) != 0 ||
183 buffer_append(&buffer, colon + 1, strlen(colon + 1)) != 0 ||
184 buffer_append(&buffer, "\n", 1) != 0) {
185 buffer_free(&buffer);
186 free(copy);
187 return NULL;
188 }
189 }
190 }
191 if (line_end == NULL) {
192 break;
193 }
194 cursor = line_end + 1;
195 }
196 if (!replaced && client_ip != NULL && client_ip[0] != '\0') {
197 if (buffer_append(&buffer, "X-Real-IP:", 10) != 0 ||
198 buffer_append(&buffer, client_ip, strlen(client_ip)) != 0 ||
199 buffer_append(&buffer, "\n", 1) != 0) {
200 buffer_free(&buffer);
201 free(copy);
202 return NULL;
203 }
204 }
205 free(copy);
206 return buffer.data != NULL ? buffer.data : pharos_http_log_strdup("");
207}
208
209const char *http_status_text_native(int status_code) {
210 switch (status_code) {
211 case 200: return "200 OK";
212 case 302: return "302 Found";
213 case 400: return "400 Bad Request";
214 case 404: return "404 Not Found";
215 case 405: return "405 Method Not Allowed";
216 case 500: return "500 Internal Server Error";
217 default: return "200 OK";
218 }
219}
220
221char *http_status_line_from_response_native(const char *response) {
222 const char *line_end;
223 const char *status_start;
224 if (response == NULL || response[0] == '\0') {
225 return pharos_http_log_strdup("500 Internal Server Error");
226 }
227 line_end = strstr(response, "\r\n");
228 if (line_end == NULL) {
229 line_end = strchr(response, '\n');
230 }
231 if (line_end == NULL) {
232 line_end = response + strlen(response);
233 }
234 status_start = strchr(response, ' ');
235 if (status_start != NULL && status_start < line_end) {
236 status_start++;
237 return dup_range(status_start, (size_t)(line_end - status_start));
238 }
239 return dup_range(response, (size_t)(line_end - response));
240}
241
242int http_status_code_from_response_native(const char *response) {
243 const char *status_start;
244 if (response == NULL || response[0] == '\0') {
245 return 500;
246 }
247 status_start = strchr(response, ' ');
248 if (status_start == NULL || status_start[1] == '\0') {
249 return 500;
250 }
251 return atoi(status_start + 1);
252}
char * compact_log_text_native(const char *value)
Compacts free-form text into a single-line log-safe representation.
char * request_real_ip_native(const char *headers)
Extracts the best client IP address from request headers.
char * request_headers_with_client_ip_native(const char *headers, const char *client_ip)
Returns request headers with an explicit client IP header applied.
#define pharos_http_log_strdup
char * http_status_line_from_response_native(const char *response)
Extracts the HTTP status line from a raw HTTP response payload.
char * mime_type_for_path(const char *path)
Resolves the MIME type that should be used for a file path.
const char * http_status_text_native(int status_code)
Returns the canonical reason phrase for one HTTP status code.
int http_status_code_from_response_native(const char *response)
Extracts the numeric HTTP status code from a raw HTTP response payload.
#define pharos_http_log_strcasecmp
Declares HTTP logging and response formatting helpers.
void buffer_init(Buffer *buffer)
Initializes a growable buffer to an empty state.
void trim_in_place(char *text)
Trims leading and trailing ASCII whitespace from a string in place.
char * dup_range(const char *start, size_t len)
Duplicates a byte range into a newly allocated NUL-terminated string.
void buffer_free(Buffer *buffer)
Releases memory owned by a growable buffer.
int buffer_append(Buffer *buffer, const void *data, size_t len)
Appends bytes to a growable buffer and maintains a trailing NUL byte.
Declares shared low-level support helpers used across the Pharos native runtime.
Growable byte buffer used by parsing and string assembly helpers.
size_t len
char * data