PT2: Initial Work

This commit is contained in:
2025-12-11 04:32:58 +00:00
parent 4983df94aa
commit 83d1b91bfa
5 changed files with 174 additions and 54 deletions
+28
View File
@@ -0,0 +1,28 @@
{
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: gcc build active file",
"command": "/usr/bin/gcc",
"args": [
"-fdiagnostics-color=always",
"-g",
"${file}",
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
],
"version": "2.0.0"
}
-1
View File
File diff suppressed because one or more lines are too long
-1
View File
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
+146 -52
View File
@@ -14,12 +14,13 @@
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// Program Settings
#define BOUND_IP "10.4.78.8"
#define PORT 8000
#define DEBUG 1 // Controls Optional Debug Output
#define DEBUG 0 // Controls Optional Debug Output
// Constants
#define REQ_HTTP_START_LINE_METHOD_LENGTH 8
@@ -43,6 +44,14 @@
#define MAX_MESSAGE_SIZE 20000
#define CACHE_MISS -1
#define CACHE_EXPIRED -2
// Globals
struct cache_entry *cache[100];
int cacheSize = 100;
int cacheCount = 0;
// Structs
struct req_http_start_line {
char method[REQ_HTTP_START_LINE_METHOD_LENGTH];
@@ -83,6 +92,15 @@ struct http_forward_info {
struct req_http_message new_message;
};
struct cache_entry {
int maxAge;
int timeSaved;
char ETag[REQ_HTTP_START_LINE_PATH_LENGTH];
char url[REQ_HTTP_START_LINE_PATH_LENGTH];
struct res_http_message *response; // AKA the body
// Cache-Control, Content-Type, Content-Length are all saved in the response.headers array
};
// Functions
// Construct REQ Start Line
@@ -108,10 +126,8 @@ struct req_http_start_line parse_req_http_start_line(char *start_line_unparsed)
start_line.version[line_end] = '\0';
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);
printf("Received Message:\n");
printf(" %s %s %s\n", start_line.method, start_line.path, start_line.version);
}
return start_line;
@@ -156,9 +172,7 @@ int parse_http_headers(char *message_str, struct http_header *headers) {
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", headers[header_count].field_name);
printf(" Field Value: %s\n", headers[header_count].field_value);
printf(" %s: %s\n" , headers[header_count].field_name, headers[header_count].field_value);
}
header_count++;
@@ -171,7 +185,7 @@ int parse_http_headers(char *message_str, struct http_header *headers) {
// Get Content-Length from headers
int get_content_length(struct res_http_message message) {
for (int i = 0; i < message.header_count; i++) {
if (strcasecmp(message.headers[i].field_name, "Content-Length") == 0) {
if (strcmp(message.headers[i].field_name, "Content-Length") == 0) {
return atoi(message.headers[i].field_value);
}
}
@@ -189,11 +203,6 @@ void parse_http_body(char *message_str, char *body) {
int body_len = strlen(body_start);
memcpy(body, body_start, body_len);
body[body_len] = '\0';
if (DEBUG) {
printf(" Parsed HTTP Body:\n");
printf(" Body Length: %d\n", body_len);
printf(" Body Content: %s\n", body);
}
}
// Parse HTTP Response Body
@@ -208,15 +217,10 @@ void parse_http_response_body(char *message_str, struct res_http_message *messag
// Use Content-Length to determine body size
message->body_length = content_length;
message->body = malloc(content_length + 1);
memcpy(message->body, body_start, content_length);
message->body[content_length] = '\0';
if (DEBUG) {
printf(" Parsed HTTP Response Body:\n");
printf(" Content-Length: %d\n", content_length);
printf(" Actual Body Length: %d\n", message->body_length);
}
}
// Construct HTTP Message from String
@@ -249,18 +253,12 @@ struct http_forward_info process_for_relay(struct req_http_message received_mess
// New Host
char *host_end = strchr(transport_protocol_end, ':');
if (host_end == NULL) {
forward_info.port = 80; // Default HTTP Port
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
if (atoi(host_end + 1) != 0) {
forward_info.port = atoi(host_end + 1);
}
forward_info.port = atoi(host_end + 1);
// New Path
char *entire_host_string_end = strchr(transport_protocol_end, '/');
@@ -283,14 +281,9 @@ struct http_forward_info process_for_relay(struct req_http_message received_mess
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);
printf("%s %s %s\n", forward_info.new_message.start_line.method, forward_info.new_message.start_line.path, forward_info.new_message.start_line.version);
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);
printf("%s %s\n", forward_info.new_message.headers[i].field_name,forward_info.new_message.headers[i].field_value);
}
}
@@ -363,10 +356,7 @@ struct res_http_start_line parse_res_http_start_line(char *start_line_unparsed)
// 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);
printf(" %s %s %s\n", start_line.version, start_line.status_code, start_line.reason_phrase);
}
return start_line;
@@ -435,11 +425,7 @@ struct res_http_message forward_message_await_response(struct http_forward_info
}
}
response_buffer[total_received] = '\0';
printf("RECEIVE: Response Received from Origin Server\n");
if (DEBUG) {
printf("FORWARD: Response Received from Origin Server (%d bytes)\n", total_received);
}
printf("RECEIVE: Response Received from Origin Server (%d bytes)\n", total_received);
struct res_http_message response_message = create_res_http_message_from_string(response_buffer);
@@ -451,6 +437,54 @@ struct res_http_message forward_message_await_response(struct http_forward_info
return response_message;
}
// Pt2: Cache Functions
int extract_max_age_from_headers(struct http_header *headers, int header_count) {
for (int i = 0; i < header_count; i++) {
if (strcmp(headers[i].field_name, "Cache-Control") == 0) {
char *max_age_str = strstr(headers[i].field_value, "max-age=");
if (max_age_str != NULL) {
return atoi(max_age_str + 8);
}
}
}
return -1; // No max-age found
}
int check_cache_for_request(struct req_http_message req_message) {
for (int i = 0; i < cacheCount; i++) {
struct cache_entry entry = *cache[i];
if (strcmp(entry.url, req_message.start_line.path) == 0) {
// Check if cache entry is still valid based on maxAge
int current_time = (int)time(NULL);
if (entry.timeSaved + entry.maxAge < current_time) {
return CACHE_EXPIRED;
}
return i; // Returns index of cache hit
}
}
return CACHE_MISS;
}
void save_response_to_cache(struct req_http_message req_message, struct res_http_message res_message) {
// Setup cache entry
struct cache_entry *new_entry = malloc(sizeof(struct cache_entry));
new_entry->response = malloc(sizeof(struct res_http_message));
// Copy RES Message into cache entry
struct res_http_message copy = res_message;
copy.body = malloc(res_message.body_length + 1);
memcpy(copy.body, res_message.body, res_message.body_length);
copy.body[res_message.body_length] = '\0';
*(new_entry->response) = copy;
strcpy(new_entry->url, req_message.start_line.path);
new_entry->maxAge = extract_max_age_from_headers(res_message.headers, res_message.header_count);
new_entry->timeSaved = (int)time(NULL);
// Add to cache
cache[cacheCount] = new_entry;
cacheCount++;
}
// Main
int main(int argc, char *argv[]) {
@@ -496,20 +530,80 @@ int main(int argc, char *argv[]) {
// Process Message From Client
printf("PROCESS: Processing Message\n");
struct req_http_message received_message = create_req_http_message_from_string(client_message);
// Determine if response comes from Cache or Origin
printf("CACHE: Checking Cache for Request\n");
int cache_hit = check_cache_for_request(received_message);
// Process Message for Relay
printf("PROCESS: Processing for Relay\n");
struct http_forward_info relay_message = process_for_relay(received_message);
struct res_http_message response;
// 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);
if (cache_hit != CACHE_MISS && cache_hit != CACHE_EXPIRED) {
printf("CACHE: HIT\n");
// Retrieve Response from Cache
response = *(cache[cache_hit]->response);
} else {
printf("CACHE: MISS/EXPIRED\n");
printf("CACHE Miss Type: %s\n", (cache_hit == CACHE_EXPIRED) ? "EXPIRED" : "MISS");
// Process Message for Relay
printf("PROCESS: Processing for Relay\n");
struct http_forward_info relay_message = process_for_relay(received_message);
if (cache_hit == CACHE_EXPIRED) {
// Add If-None-Match Header with ETag
struct http_header if_none_match_header;
strcpy(if_none_match_header.field_name, "If-None-Match");
strcpy(if_none_match_header.field_value, cache[cache_hit]->ETag);
relay_message.new_message.headers[relay_message.new_message.header_count] = if_none_match_header;
relay_message.new_message.header_count++;
}
// Forward Message to Destination Server
printf("FORWARD: Forwarding Message to %s\n", relay_message.host);
response = forward_message_await_response(relay_message);
// If 304 Not Modified, retrieve from cache
if (cache_hit == CACHE_EXPIRED && atoi(response.start_line.status_code) == 304) {
int new_max_age = extract_max_age_from_headers(response.headers, response.header_count);
cache[cache_hit]->maxAge = new_max_age;
cache[cache_hit]->timeSaved = (int)time(NULL);
// Update Cache-Control header in cache->response->headers from the string in response
for (int i = 0; i < response.header_count; i++) {
if (strcmp(response.headers[i].field_name, "Cache-Control") == 0) {
for (int j = 0; j < cache[cache_hit]->response->header_count; j++) {
if (strcmp(cache[cache_hit]->response->headers[j].field_name, "Cache-Control") == 0) {
strcpy(cache[cache_hit]->response->headers[j].field_value, response.headers[i].field_value);
}
}
}
}
response = *(cache[cache_hit]->response);
}
// Save Response to Cache
if (atoi(response.start_line.status_code) != 400) {
save_response_to_cache(received_message, response);
}
}
// Send Response Back to Client
char response_str[origin_response.body_length + MAX_MESSAGE_SIZE];
res_http_message_to_string(origin_response, response_str);
send(client_sock, response_str, origin_response.body_length + MAX_MESSAGE_SIZE, 0);
int response_str_size = response.body_length + MAX_MESSAGE_SIZE;
char *response_str = malloc(response_str_size);
memset(response_str, 0, response_str_size);
res_http_message_to_string(response, response_str);
int total_response_length = strlen(response_str);
if (response.body_length > 0) {
total_response_length = strlen(response_str) - strlen(response.body) + response.body_length;
}
printf("RESPONSE: Response Sent Back to Client\n");
send(client_sock, response_str, total_response_length, 0);
free(response_str);
// Close Client Socket
close(client_sock);