17#define PHAROS_HTTP_MAX_HEADER_BYTES (16U * 1024U)
18#define PHAROS_HTTP_MAX_REQUEST_LINE_BYTES (8U * 1024U)
19#define PHAROS_HTTP_MAX_HEADER_LINE_BYTES (4U * 1024U)
20#define PHAROS_HTTP_MAX_HEADER_COUNT 100U
21#define PHAROS_HTTP_MAX_BODY_BYTES (1U * 1024U * 1024U)
22#define PHAROS_HTTP_MAX_MULTIPART_BODY_BYTES (16U * 1024U * 1024U)
26#define native_http_request_strdup _strdup
29#include <sys/socket.h>
30#define native_http_request_strdup strdup
34 if (request == NULL) {
44 memset(request, 0,
sizeof(*request));
50 if (header_value == NULL || content_length == NULL) {
53 parsed_length = strtol(header_value, &end, 10);
54 if (end == header_value || parsed_length < 0) {
57 while (end != NULL && *end !=
'\0') {
58 if (*end !=
' ' && *end !=
'\t') {
63 *content_length = parsed_length;
73 if (header_value == NULL) {
76 for (cursor = header_value; *cursor !=
'\0'; cursor++) {
77 if ((cursor[0] ==
'c' || cursor[0] ==
'C') &&
78 (cursor[1] ==
'h' || cursor[1] ==
'H') &&
79 (cursor[2] ==
'u' || cursor[2] ==
'U') &&
80 (cursor[3] ==
'n' || cursor[3] ==
'N') &&
81 (cursor[4] ==
'k' || cursor[4] ==
'K') &&
82 (cursor[5] ==
'e' || cursor[5] ==
'E') &&
83 (cursor[6] ==
'd' || cursor[6] ==
'D')) {
93 if (temp_fd < 0 || (data == NULL && data_len > 0)) {
96 while (written < data_len) {
97 ssize_t chunk_written = write(temp_fd, data + written, data_len - written);
98 if (chunk_written <= 0) {
101 written += (size_t)chunk_written;
111 char **body_file_path_out
113 char temp_template[] =
"/tmp/pharos-body-XXXXXX";
118 if (buffer == NULL || connection == NULL || body_file_path_out == NULL || content_length < 0) {
121 *body_file_path_out = NULL;
122 temp_fd = mkstemp(temp_template);
126 initial_len = buffer->
len > body_offset ? buffer->
len - body_offset : 0;
127 if ((
long)initial_len > content_length) {
128 initial_len = (size_t)content_length;
132 unlink(temp_template);
135 written = initial_len;
136 while (written < (
size_t)content_length) {
137 size_t remaining = (size_t)content_length - written;
138 size_t to_read = remaining <
sizeof(chunk) ? remaining :
sizeof(chunk);
140 if (bytes_read <= 0) {
142 unlink(temp_template);
147 unlink(temp_template);
150 written += (size_t)bytes_read;
152 if (close(temp_fd) != 0) {
153 unlink(temp_template);
157 if (*body_file_path_out == NULL) {
158 unlink(temp_template);
169 char **body_file_path_out
173 (void)content_length;
175 if (body_file_path_out != NULL) {
176 *body_file_path_out = NULL;
184 const char *header_name,
185 const char *header_value,
186 long *content_length,
187 int *chunked_transfer
189 static const struct {
193 {
"Content-Length", 1 },
194 {
"Content-Type", 2 },
196 {
"Transfer-Encoding", 4 }
199 for (index = 0; index <
sizeof(header_map) /
sizeof(header_map[0]); index++) {
200 if (strcasecmp(header_name, header_map[index].name) != 0) {
203 switch (header_map[index].kind) {
206 *content_length = -1;
219 *chunked_transfer = 1;
233 long content_length = 0;
234 char *request_line_end;
237 char *headers_copy = NULL;
240 int chunked_transfer = 0;
241 size_t header_count = 0;
244 memset(request, 0,
sizeof(*request));
247 if (bytes_read <= 0) {
251 if (
buffer_append(&buffer, chunk, (
size_t)bytes_read) != 0) {
255 header_end = strstr(buffer.
data,
"\r\n\r\n");
256 if (header_end == NULL) {
257 header_end = strstr(buffer.
data,
"\n\n");
259 if (header_end != NULL) {
267 header_end = strstr(buffer.
data,
"\r\n\r\n");
268 if (header_end != NULL) {
269 header_len = (size_t)(header_end - buffer.
data);
270 body_offset = header_len + 4;
272 header_end = strstr(buffer.
data,
"\n\n");
273 if (header_end == NULL) {
277 header_len = (size_t)(header_end - buffer.
data);
278 body_offset = header_len + 2;
285 if (headers_copy == NULL) {
289 request_line_end = strstr(headers_copy,
"\r\n");
290 if (request_line_end == NULL) {
291 request_line_end = strchr(headers_copy,
'\n');
293 if (request_line_end == NULL) {
297 *request_line_end =
'\0';
302 space1 = strchr(headers_copy,
' ');
303 if (space1 == NULL) {
309 space2 = strchr(space1 + 1,
' ');
310 if (space2 == NULL) {
325 line = request_line_end + 1;
329 while (line != NULL && *line !=
'\0') {
330 char *next = strstr(line,
"\r\n");
332 next = strchr(line,
'\n');
345 colon = strchr(line,
':');
355 if (content_length < 0) {
362 buffer_append(&header_lines, colon + 1, strlen(colon + 1)) != 0 ||
379 if (chunked_transfer) {
385 if ((
unsigned long)content_length > (
unsigned long)body_limit) {
389 request->
body_len = content_length > 0 ? (size_t)content_length : 0;
402 while ((
long)(buffer.
len - body_offset) < content_length) {
404 if (bytes_read <= 0) {
408 if (
buffer_append(&buffer, chunk, (
size_t)bytes_read) != 0) {
422 if (parse_code != 0) {
int native_connection_read(NativeConnection *connection, void *buffer, size_t len)
Reads bytes from a native connection.
static int native_write_temp_body_chunk(int temp_fd, const char *data, size_t data_len)
#define PHAROS_HTTP_MAX_MULTIPART_BODY_BYTES
static int contains_chunked_transfer_native(const char *header_value)
#define PHAROS_HTTP_MAX_BODY_BYTES
static size_t native_http_body_limit(int is_multipart)
#define PHAROS_HTTP_MAX_REQUEST_LINE_BYTES
static int native_spool_request_body_to_temp_file(Buffer *buffer, size_t body_offset, long content_length, NativeConnection *connection, char **body_file_path_out)
void free_http_request_native(HttpRequest *request)
Releases heap-owned fields in a parsed HTTP request record.
static void apply_known_header_native(HttpRequest *request, const char *header_name, const char *header_value, long *content_length, int *chunked_transfer)
#define PHAROS_HTTP_MAX_HEADER_LINE_BYTES
#define PHAROS_HTTP_MAX_HEADER_BYTES
#define native_http_request_strdup
#define PHAROS_HTTP_MAX_HEADER_COUNT
int parse_http_request_native(NativeConnection *connection, HttpRequest *request)
Parses an HTTP request from a native connection into a request record.
static int parse_content_length_header_native(const char *header_value, long *content_length)
Declares the parsed HTTP request record and request parsing helpers.
void cleanup_native_temp_body_file(char **temp_body_path_io)
Removes a temporary request-body file and clears its stored path.
Declares temporary request-body file 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.
Parsed HTTP request fields extracted from a native connection.
Accepted client connection and its resolved transport metadata.