PT2: Initial Work
This commit is contained in:
Vendored
+28
@@ -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"
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user