Fixes
This commit is contained in:
@@ -20,7 +20,18 @@
|
||||
#define BOUND_IP "10.4.78.8"
|
||||
#define PORT 8000
|
||||
|
||||
#define DEBUG 0 // Controls Optional Debug Output
|
||||
// The below settings controls the output of the program
|
||||
// DEBUG_NONE: No output at all
|
||||
// DEBUG_INFO: Basic Output (All the DEBUG from Pt.1 of the program, plus a few extra print statements)
|
||||
// DEBUG_EXTENDED: Extra Output I used for inspecting messages being constructed / sent.
|
||||
// DEBUG_ASSIGNMENT: Output for Assignment Submission (This is the default setting I will be turning this program in with)
|
||||
#define DEBUG_MODE DEBUG_ASSIGNMENT
|
||||
|
||||
// Debug levels
|
||||
# define DEBUG_NONE 0
|
||||
# define DEBUG_INFO 1
|
||||
# define DEBUG_EXTENDED 2
|
||||
# define DEBUG_ASSIGNMENT -1
|
||||
|
||||
// Constants
|
||||
#define REQ_HTTP_START_LINE_METHOD_LENGTH 8
|
||||
@@ -46,6 +57,7 @@
|
||||
|
||||
#define CACHE_MISS -1
|
||||
#define CACHE_EXPIRED -2
|
||||
#define CACHE_HIT 1
|
||||
|
||||
// Globals
|
||||
struct cache_entry *cache[100];
|
||||
@@ -125,7 +137,7 @@ struct req_http_start_line parse_req_http_start_line(char *start_line_unparsed)
|
||||
memcpy(start_line.version, version_start, line_end);
|
||||
start_line.version[line_end] = '\0';
|
||||
|
||||
if (DEBUG) {
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) {
|
||||
printf("Received Message:\n");
|
||||
printf(" %s %s %s\n", start_line.method, start_line.path, start_line.version);
|
||||
}
|
||||
@@ -171,7 +183,7 @@ int parse_http_headers(char *message_str, struct http_header *headers) {
|
||||
current_header_unparsed[line_end - current_line] = '\0';
|
||||
|
||||
headers[header_count] = parse_http_header(current_header_unparsed);
|
||||
if (DEBUG) {
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) {
|
||||
printf(" %s: %s\n" , headers[header_count].field_name, headers[header_count].field_value);
|
||||
}
|
||||
header_count++;
|
||||
@@ -280,7 +292,7 @@ struct http_forward_info process_for_relay(struct req_http_message received_mess
|
||||
}
|
||||
forward_info.new_message.header_count = new_header_count;
|
||||
|
||||
if (DEBUG) {
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) {
|
||||
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("%s %s\n", forward_info.new_message.headers[i].field_name,forward_info.new_message.headers[i].field_value);
|
||||
@@ -355,7 +367,7 @@ struct res_http_start_line parse_res_http_start_line(char *start_line_unparsed)
|
||||
start_line.reason_phrase[line_end] = '\0';
|
||||
|
||||
// Debug: Print All Parts
|
||||
if (DEBUG) {
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) {
|
||||
printf(" %s %s %s\n", start_line.version, start_line.status_code, start_line.reason_phrase);
|
||||
}
|
||||
|
||||
@@ -395,7 +407,7 @@ struct res_http_message forward_message_await_response(struct http_forward_info
|
||||
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);
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) printf("FORWARD:\n%s\n", message_str);
|
||||
|
||||
// Create TCP Socket to Origin Server
|
||||
struct sockaddr_in sa;
|
||||
@@ -405,18 +417,18 @@ struct res_http_message forward_message_await_response(struct http_forward_info
|
||||
inet_pton(AF_INET, forward_info.host, &(sa.sin_addr));
|
||||
int recv_size = connect(my_sock, (struct sockaddr *)&sa, sizeof(sa));
|
||||
|
||||
printf("CONNECT: Origin Server Connected\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("CONNECT: Origin Server Connected\n");}
|
||||
|
||||
// Send Message to Origin Server
|
||||
send(my_sock, message_str, strlen(message_str), 0);
|
||||
if (DEBUG) printf("FORWARD: Message Sent to Origin Server\n");
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) printf("FORWARD: Message Sent to Origin Server\n");
|
||||
|
||||
// Await Response from Origin Server with dynamic buffer expansion
|
||||
memset(response_buffer, 0, buffer_size);
|
||||
int total_received = 0;
|
||||
int bytes_received;
|
||||
|
||||
while ((bytes_received = recv(my_sock, response_buffer + total_received, buffer_size - total_received - 1, 0)) > 0) {
|
||||
while ((bytes_received = recv(my_sock, response_buffer + total_received, buffer_size - total_received - 1, MSG_WAITALL)) > 0) {
|
||||
total_received += bytes_received;
|
||||
// Check if we need to expand the buffer
|
||||
if (total_received >= buffer_size - 1000) {
|
||||
@@ -426,19 +438,15 @@ struct res_http_message forward_message_await_response(struct http_forward_info
|
||||
}
|
||||
}
|
||||
|
||||
printf("Last recv returned: %d\n", bytes_received);
|
||||
printf("Buffer: \n%s\n", response_buffer);
|
||||
printf("Total Received: %d\n", total_received);
|
||||
|
||||
response_buffer[total_received] = '\0';
|
||||
printf("RECEIVE: Response Received from Origin Server (%d bytes)\n", total_received);
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {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);
|
||||
|
||||
// Clean up
|
||||
free(response_buffer);
|
||||
close(my_sock);
|
||||
printf("DISCONNECT: Origin Server Disconnected\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("DISCONNECT: Origin Server Disconnected\n");}
|
||||
|
||||
return response_message;
|
||||
}
|
||||
@@ -456,21 +464,28 @@ int extract_max_age_from_headers(struct http_header *headers, int header_count)
|
||||
return -1; // No max-age found
|
||||
}
|
||||
|
||||
// Check Cache for Request and Return Index if Hit
|
||||
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;
|
||||
}
|
||||
|
||||
int check_cache_if_valid(int cache_index) {
|
||||
struct cache_entry entry = *cache[cache_index];
|
||||
int current_time = (int)time(NULL);
|
||||
if (entry.timeSaved + entry.maxAge < current_time) {
|
||||
return CACHE_EXPIRED;
|
||||
}
|
||||
return 1; // Valid
|
||||
}
|
||||
|
||||
// Save Response to Cache
|
||||
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));
|
||||
@@ -485,23 +500,78 @@ void save_response_to_cache(struct req_http_message req_message, struct res_http
|
||||
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);
|
||||
// Extract and save ETag from response headers
|
||||
for (int i = 0; i < res_message.header_count; i++) {
|
||||
if (strcmp(res_message.headers[i].field_name, "ETag") == 0) {
|
||||
strcpy(new_entry->ETag, res_message.headers[i].field_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("[Info] Caching object with Max-Age = %d, ETag = \"%s\"\n", new_entry->maxAge, new_entry->ETag);
|
||||
|
||||
// Add to cache
|
||||
cache[cacheCount] = new_entry;
|
||||
|
||||
// Look in the cache for a free spot. Also can use first expired spot
|
||||
int placed = 0;
|
||||
|
||||
for (int i = 0; i < cacheCount; i++) {
|
||||
struct cache_entry entry = *cache[i];
|
||||
int current_time = (int)time(NULL);
|
||||
if (entry.timeSaved + entry.maxAge < current_time) {
|
||||
// Found expired spot, replace it
|
||||
cache[i] = new_entry;
|
||||
placed = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed) {
|
||||
cache[cacheCount] = new_entry;
|
||||
}
|
||||
|
||||
cacheCount++;
|
||||
}
|
||||
|
||||
// Check if Response can be Cached
|
||||
int can_cache(struct res_http_message message) {
|
||||
for (int i = 0; i < message.header_count; i++) {
|
||||
if (strcmp(message.headers[i].field_name, "Cache-Control") == 0) {
|
||||
if (strstr(message.headers[i].field_value, "no-store") != NULL) {
|
||||
return 0; // Cannot cache
|
||||
}
|
||||
}
|
||||
}
|
||||
return CACHE_HIT; // Can cache
|
||||
}
|
||||
|
||||
// Debug Print Functions
|
||||
void print_req_http_message(struct req_http_message message) {
|
||||
printf("[Info] \t\t%s %s %s\n", message.start_line.method, message.start_line.path, message.start_line.version);
|
||||
for (int i = 0; i < message.header_count; i++) {
|
||||
printf("[Info] \t\t%s: %s\n", message.headers[i].field_name, message.headers[i].field_value);
|
||||
}
|
||||
}
|
||||
|
||||
void print_res_http_message(struct res_http_message message) {
|
||||
printf("[Info] \t\t%s %s %s\n", message.start_line.version, message.start_line.status_code, message.start_line.reason_phrase);
|
||||
for (int i = 0; i < message.header_count; i++) {
|
||||
printf("[Info] \t\t%s: %s\n", message.headers[i].field_name, message.headers[i].field_value);
|
||||
}
|
||||
printf("[Info] \t\tBody Length: %d\n", message.body_length);
|
||||
}
|
||||
|
||||
// Main
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
// Setup TCP Socket
|
||||
if (DEBUG) printf("SETUP: Setup Sockets\n");
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) printf("SETUP: Setup Sockets\n");
|
||||
int serv_sock, client_sock, read_size;
|
||||
struct sockaddr_in server, client;
|
||||
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");
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) printf("SETUP: Socket Created\n");
|
||||
|
||||
// Bind Sockets
|
||||
server.sin_family = AF_INET;
|
||||
@@ -515,48 +585,74 @@ int main(int argc, char *argv[]) {
|
||||
perror("BIND FAILED");
|
||||
return 1;
|
||||
}
|
||||
if (DEBUG) printf("SETUP: Socket Bound\n");
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) printf("SETUP: Socket Bound\n");
|
||||
|
||||
// Start Listening and Waiting for Connections
|
||||
listen(serv_sock, 1);
|
||||
if (DEBUG) printf("SETUP: Listening for Connections\n\n");
|
||||
if (DEBUG_MODE >= DEBUG_EXTENDED) printf("SETUP: Listening for Connections\n\n");
|
||||
|
||||
while (1) {
|
||||
// Accept Connection from Client
|
||||
printf("WAIT: Waiting for incoming connections...\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("WAIT: Waiting for incoming connections...\n");}
|
||||
int c = sizeof(struct sockaddr_in);
|
||||
client_sock = accept(serv_sock, (struct sockaddr *)&client, &c);
|
||||
printf("CONNECT: Client Connected\n");
|
||||
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("CONNECT: Client Connected\n");}
|
||||
|
||||
// Receive Message from Client
|
||||
memset(client_message, 0, sizeof(client_message));
|
||||
read_size = recv(client_sock, client_message, sizeof(client_message), 0);
|
||||
printf("RECEIVE: Message Received\n");
|
||||
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("RECEIVE: Message Received\n");}
|
||||
|
||||
// Process Message From Client
|
||||
printf("PROCESS: Processing Message\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("PROCESS: Processing Message\n");}
|
||||
struct req_http_message received_message = create_req_http_message_from_string(client_message);
|
||||
|
||||
// DEBUG: Print Received Message
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] [REQ] Received Message from Client:\n");
|
||||
print_req_http_message(received_message);
|
||||
}
|
||||
|
||||
// Determine if response comes from Cache or Origin
|
||||
printf("CACHE: Checking Cache for Request\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("CACHE: Checking Cache for Request\n");}
|
||||
int cache_hit = check_cache_for_request(received_message);
|
||||
int cache_valid;
|
||||
if (cache_hit != CACHE_MISS) {
|
||||
cache_valid = check_cache_if_valid(cache_hit);
|
||||
}
|
||||
|
||||
struct res_http_message response;
|
||||
|
||||
if (cache_hit != CACHE_MISS && cache_hit != CACHE_EXPIRED) {
|
||||
printf("CACHE: HIT\n");
|
||||
if (cache_hit != CACHE_MISS && cache_valid != CACHE_EXPIRED) {
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("CACHE: HIT\n");}
|
||||
|
||||
// Debug: Print Assignment Case 2 Message
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] Cache Hit! Object in cache and fresh (Max-Age = %d, in cache %d)\n", cache[cache_hit]->maxAge, cache_hit);
|
||||
printf("[Info] Returning cached object\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");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("CACHE: MISS/EXPIRED\n");}
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("CACHE Miss Type: %s\n", (cache_valid == CACHE_EXPIRED) ? "EXPIRED" : "MISS");}
|
||||
|
||||
// Debug: Print Assignment Case 1 and 2 Messages
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT && cache_valid == CACHE_EXPIRED) {
|
||||
// Cache Miss (Case 2)
|
||||
printf("[Info] Cache Miss! Object in cache but stale (Max-Age = %d, in cache %d)\n", cache[cache_hit]->maxAge, cache_hit);
|
||||
printf("[Info] Must revalidate with server using If-None-Match: \"%s\"\n", cache[cache_hit]->ETag);
|
||||
} else {
|
||||
// Cache Miss (Case 1)
|
||||
printf("[Info] Object not in cache. Contacting server\n");
|
||||
}
|
||||
|
||||
// Process Message for Relay
|
||||
printf("PROCESS: Processing for Relay\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("PROCESS: Processing for Relay\n");}
|
||||
struct http_forward_info relay_message = process_for_relay(received_message);
|
||||
|
||||
if (cache_hit == CACHE_EXPIRED) {
|
||||
if (cache_valid == 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");
|
||||
@@ -566,15 +662,30 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// Forward Message to Destination Server
|
||||
printf("FORWARD: Forwarding Message to %s\n", relay_message.host);
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("FORWARD: Forwarding Message to %s\n", relay_message.host);}
|
||||
|
||||
// DEBUG: Print Forwarded Message
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] [REQ] Forwarded Message to Origin Server:\n");
|
||||
print_req_http_message(relay_message.new_message);
|
||||
}
|
||||
response = forward_message_await_response(relay_message);
|
||||
|
||||
// DEBUG: Print Received Response Message
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] [RES] Received Message from Origin Server:\n");
|
||||
print_res_http_message(response);
|
||||
printf("[Info] Response Status: %d %s\n", atoi(response.start_line.status_code), response.start_line.reason_phrase);
|
||||
}
|
||||
|
||||
// If 304 Not Modified, retrieve from cache
|
||||
if (cache_hit == CACHE_EXPIRED && atoi(response.start_line.status_code) == 304) {
|
||||
if (cache_valid == CACHE_EXPIRED && atoi(response.start_line.status_code) == 304) {
|
||||
printf("[Info] Received 304 Not Modified from server\n");
|
||||
|
||||
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) {
|
||||
@@ -585,15 +696,31 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
response = *(cache[cache_hit]->response);
|
||||
}
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] Returning cached object with 200 OK\n");
|
||||
}
|
||||
|
||||
// Save Response to Cache
|
||||
if (atoi(response.start_line.status_code) != 400) {
|
||||
save_response_to_cache(received_message, response);
|
||||
if (can_cache(response)) {
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] Object is cacheable\n");
|
||||
}
|
||||
|
||||
if (atoi(response.start_line.status_code) != 400) {
|
||||
save_response_to_cache(received_message, response);
|
||||
}
|
||||
} else if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] Object is not cacheable\n");
|
||||
}
|
||||
}
|
||||
// DEBUG: Print Response Message
|
||||
if (DEBUG_MODE == DEBUG_ASSIGNMENT) {
|
||||
printf("[Info] [RES] Sending Response to Client:\n");
|
||||
print_res_http_message(response);
|
||||
}
|
||||
|
||||
// Send Response Back to Client
|
||||
int response_str_size = response.body_length + MAX_MESSAGE_SIZE;
|
||||
@@ -606,14 +733,14 @@ int main(int argc, char *argv[]) {
|
||||
total_response_length = strlen(response_str) - strlen(response.body) + response.body_length;
|
||||
}
|
||||
|
||||
printf("RESPONSE: Response Sent Back to Client\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {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);
|
||||
printf("DISCONNECT: Client Disconnected\n\n");
|
||||
if (DEBUG_MODE >= DEBUG_INFO) {printf("DISCONNECT: Client Disconnected\n\n");}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user