First Working Version

This commit is contained in:
2025-12-02 22:22:34 +00:00
parent 98afbb60e9
commit 7946387f93
2 changed files with 418 additions and 24 deletions
Regular → Executable
BIN
View File
Binary file not shown.
+418 -24
View File
@@ -12,36 +12,409 @@
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
// Program Settings
#define BOUND_IP "10.4.78.8"
#define PORT 5000
#define DEBUG 1 // Controls Optional Debug Output
// Constants
#define PORT 5000
#define DEBUG 0
#define REQ_HTTP_START_LINE_METHOD_LENGTH 8
#define REQ_HTTP_START_LINE_PATH_LENGTH 1024
#define REQ_HTTP_START_LINE_VERSION_LENGTH 16
#define REQ_HTTP_START_LINE_EXTRA_CHARS 4
#define REQ_HTTP_START_LINE_SEPARATOR ' '
// Globals
#define RES_HTTP_START_LINE_VERSION_LENGTH 16
#define RES_HTTP_START_LINE_STATUS_CODE_LENGTH 4
#define RES_HTTP_START_LINE_REASON_PHRASE_LENGTH 64
#define HTTP_HEADER_FIELD_SEPARATOR ": "
#define HTTP_HEADER_FIELD_NAME_LENGTH 256
#define HTTP_HEADER_FIELD_VALUE_LENGTH 1024
#define HTTP_HEADER_FIELD_EXTRA_CHARS 4
#define HTTP_HEADER_LINE_ENDING "\r\n"
#define HTTP_MESSAGE_MAX_HEADERS 100
#define HTTP_MESSAGE_BODY_LENGTH 8192
#define HTTP_BODY_SEPARATOR "\r\n\r\n"
#define MAX_MESSAGE_SIZE 20000
#define MAX_MESSAGE_COUNT 10
// Structs
struct http_start_line {
char method[8];
char path[1024];
char version[16];
struct req_http_start_line {
char method[REQ_HTTP_START_LINE_METHOD_LENGTH];
char path[REQ_HTTP_START_LINE_PATH_LENGTH];
char version[REQ_HTTP_START_LINE_VERSION_LENGTH];
};
struct res_http_start_line {
char version[RES_HTTP_START_LINE_VERSION_LENGTH];
char status_code[RES_HTTP_START_LINE_STATUS_CODE_LENGTH];
char reason_phrase[RES_HTTP_START_LINE_REASON_PHRASE_LENGTH];
};
struct http_header {
char field_name[256];
char field_value[1024];
char field_name[HTTP_HEADER_FIELD_NAME_LENGTH];
char field_value[HTTP_HEADER_FIELD_VALUE_LENGTH];
};
struct http_message {
struct http_start_line start_line;
struct http_header headers[100];
char body[8192];
struct req_http_message {
struct req_http_start_line start_line;
struct http_header headers[HTTP_MESSAGE_MAX_HEADERS];
int header_count;
char body[HTTP_MESSAGE_BODY_LENGTH];
};
struct res_http_message {
struct res_http_start_line start_line;
struct http_header headers[HTTP_MESSAGE_MAX_HEADERS];
int header_count;
char body[HTTP_MESSAGE_BODY_LENGTH];
};
struct http_forward_info {
char host[REQ_HTTP_START_LINE_PATH_LENGTH];
int port;
char transport_protocol[5];
struct req_http_message new_message;
};
// Functions
struct http_message create_http_message_from_string(char *message_str) {
struct http_message message;
// Construct REQ Start Line
struct req_http_start_line parse_req_http_start_line(char *start_line_unparsed) {
struct req_http_start_line start_line;
// Method
int first_space_seperator = strchr(start_line_unparsed, REQ_HTTP_START_LINE_SEPARATOR) - start_line_unparsed;
memcpy(start_line.method, start_line_unparsed, first_space_seperator);
start_line.method[first_space_seperator] = '\0';
// Path
char *path_start = start_line_unparsed + first_space_seperator + 1;
int second_space_seperator = strchr(path_start, REQ_HTTP_START_LINE_SEPARATOR) - path_start;
memcpy(start_line.path, path_start, second_space_seperator);
start_line.path[second_space_seperator] = '\0';
// Version
char *version_start = path_start + second_space_seperator + 1;
int line_end = strchr(version_start, '\r') - version_start;
memcpy(start_line.version, version_start, line_end);
start_line.version[line_end] = '\0';
// Debug: Print All Parts
if (DEBUG) {
printf(" Parsed HTTP Start Line:\n");
printf(" Method: %s\n", start_line.method);
printf(" Path: %s\n", start_line.path);
printf(" Version: %s\n", start_line.version);
}
return start_line;
}
// Construct Header from String
struct http_header parse_http_header(char *header_str) {
struct http_header header;
// Header Field Name
char *separator_pos = strstr(header_str, HTTP_HEADER_FIELD_SEPARATOR);
int field_name_len = separator_pos - header_str;
memcpy(header.field_name, header_str, field_name_len);
header.field_name[field_name_len] = '\0';
// Header Field Value
char *field_value_start = separator_pos + strlen(HTTP_HEADER_FIELD_SEPARATOR);
int field_value_len = strlen(field_value_start);
memcpy(header.field_value, field_value_start, field_value_len);
header.field_value[field_value_len] = '\0';
return header;
}
// Construct HTTP Message from String
struct req_http_message create_req_http_message_from_string(char *message_str) {
struct req_http_message message;
// Parse Start Line
int start_line_len = REQ_HTTP_START_LINE_METHOD_LENGTH + REQ_HTTP_START_LINE_PATH_LENGTH + REQ_HTTP_START_LINE_VERSION_LENGTH + REQ_HTTP_START_LINE_EXTRA_CHARS;
char start_line_unparsed[start_line_len];
memcpy(start_line_unparsed, message_str, start_line_len);
message.start_line = parse_req_http_start_line(start_line_unparsed);
// Parse Headers
// Find start of headers (after first \r\n)
char *headers_start = strstr(message_str, HTTP_HEADER_LINE_ENDING) + 2;
char *headers_end = strstr(headers_start, HTTP_BODY_SEPARATOR);
// Parse each header line
char *current_line = headers_start;
int header_count = 0;
while (current_line < headers_end && header_count < HTTP_MESSAGE_MAX_HEADERS) {
char *line_end = strstr(current_line, HTTP_HEADER_LINE_ENDING);
if (line_end == NULL || line_end > headers_end) break;
char current_header_unparsed[HTTP_HEADER_FIELD_NAME_LENGTH + HTTP_HEADER_FIELD_VALUE_LENGTH + 4];
memcpy(current_header_unparsed, current_line, line_end - current_line);
current_header_unparsed[line_end - current_line] = '\0';
message.headers[header_count] = parse_http_header(current_header_unparsed);
if (DEBUG) {
printf(" Parsed HTTP Header %d:\n", header_count + 1);
printf(" Field Name: %s\n", message.headers[header_count].field_name);
printf(" Field Value: %s\n", message.headers[header_count].field_value);
}
header_count++;
current_line = line_end + strlen(HTTP_HEADER_LINE_ENDING);
}
message.header_count = header_count;
// Parse body
char *body_start = headers_end + strlen(HTTP_BODY_SEPARATOR);
int body_len = strlen(body_start);
memcpy(message.body, body_start, body_len);
message.body[body_len] = '\0';
if (DEBUG) {
printf(" Parsed HTTP Body:\n");
printf(" Body Length: %d\n", body_len);
printf(" Body Content: %s\n", message.body);
}
return message;
}
// Process Message for Relay
struct http_forward_info process_for_relay(struct req_http_message received_message) {
struct http_forward_info forward_info;
forward_info.new_message = received_message;
// New Transport Protocol
char *original_host = memcpy(received_message.start_line.path, received_message.start_line.path, strlen(received_message.start_line.path));
char *transport_protocol_end = strstr(original_host, "://") + 3;
memcpy(forward_info.transport_protocol, original_host, transport_protocol_end - original_host);
// New Host
char *host_end = strchr(transport_protocol_end, ':');
char *port_end = strchr(host_end, '/');
memcpy(forward_info.host, transport_protocol_end, host_end - transport_protocol_end);
forward_info.host[host_end - transport_protocol_end] = '\0';
// New Port
forward_info.port = atoi(host_end + 1);
printf(" Extracted Port: %d\n", forward_info.port);
// New Path
char *entire_host_string_end = strchr(transport_protocol_end, '/');
memcpy(forward_info.new_message.start_line.path, entire_host_string_end, strlen(entire_host_string_end));
forward_info.new_message.start_line.path[strlen(entire_host_string_end)] = '\0';
// Strip out Proxy-Connection Header and replace with Connection: Close
int new_header_count = 0;
for (int i = 0; i < forward_info.new_message.header_count; i++) {
if (strcmp(forward_info.new_message.headers[i].field_name, "Proxy-Connection") == 0) {
// Replace Proxy-Connection with Connection: close
strcpy(forward_info.new_message.headers[new_header_count].field_name, "Connection");
strcpy(forward_info.new_message.headers[new_header_count].field_value, "Close");
new_header_count++;
} else {
// Keep the existing header
forward_info.new_message.headers[new_header_count] = forward_info.new_message.headers[i];
new_header_count++;
}
}
forward_info.new_message.header_count = new_header_count;
if (DEBUG) {
printf(" Processed Forward Info:\n");
printf(" Host: %s\n", forward_info.host);
printf(" Port: %d\n", forward_info.port);
printf(" New Path: %s\n", forward_info.new_message.start_line.path);
for (int i = 0; i < forward_info.new_message.header_count; i++) {
printf(" Header %d: %s: %s\n", i + 1,
forward_info.new_message.headers[i].field_name,
forward_info.new_message.headers[i].field_value);
}
}
return forward_info;
}
// Convert HTTP REQ Message Struct to String
void req_http_message_to_string(struct req_http_message message, char *message_str) {
// Start Line
sprintf(message_str, "%s %s %s%s", message.start_line.method, message.start_line.path, message.start_line.version, HTTP_HEADER_LINE_ENDING);
// Headers
for (int i = 0; i < message.header_count; i++) {
strcat(message_str, message.headers[i].field_name);
strcat(message_str, HTTP_HEADER_FIELD_SEPARATOR);
strcat(message_str, message.headers[i].field_value);
strcat(message_str, HTTP_HEADER_LINE_ENDING);
}
// End of Headers
strcat(message_str, HTTP_HEADER_LINE_ENDING);
// Body
strcat(message_str, message.body);
}
// Convert HTTP RES Message Struct to String
void res_http_message_to_string(struct res_http_message message, char *message_str) {
// Start Line
sprintf(message_str, "%s %s %s%s", message.start_line.version, message.start_line.status_code, message.start_line.reason_phrase, HTTP_HEADER_LINE_ENDING);
// Headers
for (int i = 0; i < message.header_count; i++) {
strcat(message_str, message.headers[i].field_name);
strcat(message_str, HTTP_HEADER_FIELD_SEPARATOR);
strcat(message_str, message.headers[i].field_value);
strcat(message_str, HTTP_HEADER_LINE_ENDING);
}
// End of Headers
strcat(message_str, HTTP_HEADER_LINE_ENDING);
// Body
strcat(message_str, message.body);
}
// Construct RES Start Line
struct res_http_start_line parse_res_http_start_line(char *start_line_unparsed) {
struct res_http_start_line start_line;
// Version
int first_space_seperator = strchr(start_line_unparsed, REQ_HTTP_START_LINE_SEPARATOR) - start_line_unparsed;
memcpy(start_line.version, start_line_unparsed, first_space_seperator);
start_line.version[first_space_seperator] = '\0';
// Status Code
char *status_code_start = start_line_unparsed + first_space_seperator + 1;
int second_space_seperator = strchr(status_code_start, REQ_HTTP_START_LINE_SEPARATOR) - status_code_start;
memcpy(start_line.status_code, status_code_start, second_space_seperator);
start_line.status_code[second_space_seperator] = '\0';
// Reason Phrase
char *reason_phrase_start = status_code_start + second_space_seperator + 1;
int line_end = strchr(reason_phrase_start, '\r') - reason_phrase_start;
memcpy(start_line.reason_phrase, reason_phrase_start, line_end);
start_line.reason_phrase[line_end] = '\0';
// Debug: Print All Parts
if (DEBUG) {
printf(" Parsed HTTP Start Line:\n");
printf(" Version: %s\n", start_line.version);
printf(" Status Code: %s\n", start_line.status_code);
printf(" Reason Phrase: %s\n", start_line.reason_phrase);
}
return start_line;
}
// Construct HTTP RES Message from String
struct res_http_message create_res_http_message_from_string(char *message_str) {
struct res_http_message message;
// Parse Start Line
int start_line_len = RES_HTTP_START_LINE_VERSION_LENGTH + RES_HTTP_START_LINE_STATUS_CODE_LENGTH + RES_HTTP_START_LINE_REASON_PHRASE_LENGTH + REQ_HTTP_START_LINE_EXTRA_CHARS;
char start_line_unparsed[start_line_len];
memcpy(start_line_unparsed, message_str, start_line_len);
message.start_line = parse_res_http_start_line(start_line_unparsed);
// Parse Headers
// Find start of headers (after first \r\n)
char *headers_start = strstr(message_str, HTTP_HEADER_LINE_ENDING) + 2;
char *headers_end = strstr(headers_start, HTTP_BODY_SEPARATOR);
// Parse each header line
char *current_line = headers_start;
int header_count = 0;
while (current_line < headers_end && header_count < HTTP_MESSAGE_MAX_HEADERS) {
char *line_end = strstr(current_line, HTTP_HEADER_LINE_ENDING);
if (line_end == NULL || line_end > headers_end) break;
char current_header_unparsed[HTTP_HEADER_FIELD_NAME_LENGTH + HTTP_HEADER_FIELD_VALUE_LENGTH + 4];
memcpy(current_header_unparsed, current_line, line_end - current_line);
current_header_unparsed[line_end - current_line] = '\0';
message.headers[header_count] = parse_http_header(current_header_unparsed);
if (DEBUG) {
printf(" Parsed HTTP Header %d:\n", header_count + 1);
printf(" Field Name: %s\n", message.headers[header_count].field_name);
printf(" Field Value: %s\n", message.headers[header_count].field_value);
}
header_count++;
current_line = line_end + strlen(HTTP_HEADER_LINE_ENDING);
}
message.header_count = header_count;
// Parse body
char *body_start = headers_end + strlen(HTTP_BODY_SEPARATOR);
int body_len = strlen(body_start);
memcpy(message.body, body_start, body_len);
message.body[body_len] = '\0';
if (DEBUG) {
printf(" Parsed HTTP Body:\n");
printf(" Body Length: %d\n", body_len);
printf(" Body Content: %s\n", message.body);
}
return message;
}
// Forward Message and Await Response
struct res_http_message forward_message_await_response(struct http_forward_info forward_info) {
char response_buffer[MAX_MESSAGE_SIZE];
// Convert HTTP Message Struct to String
char message_str[MAX_MESSAGE_SIZE];
req_http_message_to_string(forward_info.new_message, message_str);
if (DEBUG) printf("FORWARD:\n%s\n", message_str);
// Create TCP Socket to Origin Server
struct sockaddr_in sa;
int my_sock = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_family = AF_INET;
sa.sin_port = htons(forward_info.port);
inet_pton(AF_INET, forward_info.host, &(sa.sin_addr));
int recv_size = connect(my_sock, (struct sockaddr *)&sa, sizeof(sa));
// Send Message to Origin Server
send(my_sock, message_str, strlen(message_str), 0);
if (DEBUG) printf("FORWARD: Message Sent to Origin Server\n");
// Await Response from Origin Server
memset(response_buffer, 0, MAX_MESSAGE_SIZE);
int total_received = 0;
int bytes_received;
while ((bytes_received = recv(my_sock, response_buffer + total_received, MAX_MESSAGE_SIZE - total_received - 1, 0)) > 0) {
total_received += bytes_received;
}
if (DEBUG) {
printf("FORWARD: Response Received from Origin Server (%d bytes)\n", total_received);
printf("RESPONSE:\n");
printf("%s\n", response_buffer);
}
struct res_http_message response_message = create_res_http_message_from_string(response_buffer);
close(my_sock);
return response_message;
}
// Main
@@ -51,7 +424,7 @@ int main(int argc, char *argv[]) {
if (DEBUG) printf("SETUP: Setup Sockets\n");
int serv_sock, client_sock, read_size;
struct sockaddr_in server, client;
char client_message[20000];
char client_message[MAX_MESSAGE_SIZE];
serv_sock = socket(AF_INET, SOCK_STREAM, 0);
client_sock = socket(AF_INET, SOCK_STREAM, 0);
if (DEBUG) printf("SETUP: Socket Created\n");
@@ -59,8 +432,15 @@ int main(int argc, char *argv[]) {
// Bind Sockets
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
// inet_pton(AF_INET, INADDR_ANY, &(server.sin_addr));
bind(serv_sock,(struct sockaddr *)&server, sizeof(server));
inet_pton(AF_INET, BOUND_IP, &(server.sin_addr));
// TODO: REMOVE THIS BEFORE SUBMISSION
setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, &server, sizeof(server));
if (bind(serv_sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
perror("BIND FAILED");
return 1;
}
if (DEBUG) printf("SETUP: Socket Bound\n");
// Start Listening and Waiting for Connections
@@ -69,23 +449,37 @@ int main(int argc, char *argv[]) {
while (1) {
// Accept Connection from Client
if (DEBUG) printf("WAIT: Waiting for incoming connections...\n");
printf("WAIT: Waiting for incoming connections...\n");
int c = sizeof(struct sockaddr_in);
client_sock = accept(serv_sock, (struct sockaddr *)&client, &c);
if (DEBUG) printf("CONNECT: Connection Accepted\n");
printf("CONNECT: Connection Accepted\n");
// Receive Message from Client
memset(client_message, 0, sizeof(client_message));
read_size = recv(client_sock, client_message, sizeof(client_message), 0);
if (DEBUG) printf("RECEIVE: Message Received\n");
printf("RECEIVE: Message Received\n");
// Process Message From Client
if (DEBUG) printf("PROCESS: Processing Message\n");
struct http_message message = create_http_message_from_string(client_message);
printf("PROCESS: Processing Message\n");
struct req_http_message received_message = create_req_http_message_from_string(client_message);
// Process Message for Relay
printf("PROCESS: Processing for Relay\n");
struct http_forward_info relay_message = process_for_relay(received_message);
// Forward Message to Destination Server
printf("FORWARD: Forwarding Message to %s\n", relay_message.host);
struct res_http_message origin_response = forward_message_await_response(relay_message);
// Send Response Back to Client
char response_str[MAX_MESSAGE_SIZE];
res_http_message_to_string(origin_response, response_str);
send(client_sock, response_str, strlen(response_str), 0);
printf("RESPONSE: Response Sent Back to Client\n");
// Close Client Socket
close(client_sock);
if (DEBUG) printf("DISCONNECT: Client Disconnected\n\n");
printf("DISCONNECT: Client Disconnected\n\n");
}
return 0;