This commit is contained in:
2025-03-21 10:59:34 +10:00
commit 32e2a09b8d
7 changed files with 1062 additions and 0 deletions

18
.clang-format Normal file
View File

@@ -0,0 +1,18 @@
---
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 79
TabWidth: 8
ContinuationIndentWidth: 8
UseTab: Never
PointerAlignment: Right
DerivePointerAlignment: false
AlignAfterOpenBracket: DontAlign
AlignTrailingComments: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
IncludeBlocks: Regroup
BreakBeforeBraces: Attach
---

27
Makefile Normal file
View File

@@ -0,0 +1,27 @@
CC=gcc
CFLAGS=-std=gnu99 -Wall -pedantic -I/local/courses/csse2310/include
LIBCFLAGS=-fPIC $(CFLAGS)
LDFLAGS=-L/local/courses/csse2310/lib -lcsse2310a4
.PHONY: all debug profile clean
.DEFAULT_GOAL := all
all: dbclient dbserver libstringstore.so
debug: CFLAGS += -g
debug: all
profile: CFLAGS += -pg -fprofile-arcs -ftest-coverage
profile: all
dbserver: dbserver.c dbserver.h
$(CC) $(CFLAGS) -pthread $(LDFLAGS) -lcsse2310a3 -lstringstore -o $@ $^
stringstore.o: stringstore.c /local/courses/csse2310/include/stringstore.h
$(CC) $(LIBCFLAGS) -c $<
libstringstore.so: stringstore.o
$(CC) -shared -o $@ stringstore.o
clean:
rm -f dbclient dbserver libstringstore.so *.o

151
dbclient.c Normal file
View File

@@ -0,0 +1,151 @@
#include "dbclient.h"
char *construct_header(char *key, char *value) {
char *header = NULL;
// If there is no value, make a get request, otherwise make a put request
// with that value
if (!value) {
size_t needed_size = snprintf(NULL, 0, GET_REQUEST, key);
header = (char *)malloc(needed_size + sizeof(char));
sprintf(header, GET_REQUEST, key);
} else {
size_t body_size = strlen(value) * sizeof(char);
size_t needed_size =
snprintf(NULL, 0, PUT_REQUEST, key, body_size, value);
header = (char *)malloc(needed_size + sizeof(char));
sprintf(header, PUT_REQUEST, key, body_size, value);
}
return header;
}
int handle_connect(char *port, int *fd) {
struct addrinfo *ai = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
// Set hints
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
// Get address information
int err;
if ((err = getaddrinfo("localhost", port, &hints, &ai))) {
freeaddrinfo(ai);
fprintf(stderr, "%s\n", gai_strerror(err));
return EXIT_ERR;
}
// Connect to port
*fd = socket(AF_INET, SOCK_STREAM, 0);
// clang-format off
if (connect(*fd, (struct sockaddr *)ai->ai_addr,
sizeof(struct sockaddr))) {
// clang-format on
freeaddrinfo(ai);
close(*fd);
fprintf(stderr, "dbclient: unable to connect to port %s\n", port);
return EXIT_PORT_ERR;
}
freeaddrinfo(ai);
return EXIT_OK;
}
int handle_response(FILE *recv, int valueSet) {
HttpHeader **responseHeaders = NULL;
memset(&responseHeaders, 0, sizeof(HttpHeader *));
int status;
char *statusExplain = NULL;
char *body = NULL;
get_HTTP_response(recv, &status, &statusExplain, &responseHeaders, &body);
// Print the value only if GET and got status 200
if (!valueSet && status == 200) {
printf("%s\n", body);
}
free_array_of_headers(responseHeaders);
free(statusExplain);
free(body);
if (status != 200 && valueSet) {
return EXIT_PUT_ERR;
} else if (status != 200) {
return EXIT_GET_ERR;
}
return EXIT_OK;
}
int handle_connection(char *port, char *key, char *value) {
int connectStatus = 0;
int sendFd;
if ((connectStatus = handle_connect(port, &sendFd))) {
close(sendFd);
return connectStatus;
}
// Setup fd's for sending and receiving requests
int recvFd = dup(sendFd);
FILE *send = fdopen(sendFd, "w");
FILE *recv = fdopen(recvFd, "r");
// Send Request
char *header = construct_header(key, value);
fprintf(send, "%s", header);
fflush(send);
fclose(send);
free(header);
close(sendFd);
// Get Response
int valueSet = (value != NULL);
int exitCode = handle_response(recv, valueSet);
fclose(recv);
close(recvFd);
return exitCode;
}
int is_valid_key(char *key) {
for (int i = 0; i < strlen(key); i++) {
if (isspace(key[i])) {
return 0;
}
}
return 1;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr, "Usage: dbclient portnum key [value]\n");
return EXIT_ERR;
}
char *portnumArgument = strdup(argv[1]);
char *keyArgument = strdup(argv[2]);
char *valueArgument = NULL;
if (argc > 3) {
valueArgument = strdup(argv[3]);
}
if (!is_valid_key(keyArgument)) {
fprintf(stderr, "dbclient: key must not contain spaces or newlines\n");
return EXIT_ERR;
}
int exitStatus =
handle_connection(portnumArgument, keyArgument, valueArgument);
free(portnumArgument);
free(keyArgument);
free(valueArgument);
return exitStatus;
}

81
dbclient.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef DBCLIENT_H
#define DBCLIENT_H
#include <csse2310a4.h>
#include <ctype.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define GET_REQUEST "GET /public/%s HTTP/1.1\r\n\r\n"
#define PUT_REQUEST "PUT /public/%s HTTP/1.1\r\nContent-Length: %lu\r\n\r\n%s"
// Program exit codes
enum ExitCodes {
EXIT_OK = 0,
EXIT_ERR = 1,
EXIT_PORT_ERR = 2,
EXIT_GET_ERR = 3,
EXIT_PUT_ERR = 4
};
/* construct_header()
* ------------------
* Constructs the needed header to be sent
*
* key: the to check or update
* value: the value to assign to the key
*
* Returns: the header
*/
char *construct_header(char *key, char *value);
/* handle_connect()
* ----------------
* Handles the initial connection to the port given, updates the given
* file descriptors pointer to the created one
*
* port: the port number or service to connect to
* fd: the file descriptor that will be connected for the connection
*
* Returns: 1 if the address info couldnt be found, 2 if could not connect to
* port, 0 if successful
*/
int handle_connect(char *port, int *fd);
/* handle_response()
* -----------------
* Handles the response given from the connected server
*
* recv: the fd that will be used for reading
* valueSet: if the value argument was set
*
* Returns: 3 if GET failed, 4 if PUT failed, 0 if successful
*/
int handle_response(FILE *recv, int valueSet);
/* handle_connection()
* -------------------
* Handles the connection to the given port, sends a GET or PUT request
* depending on if the value argument is not NULL
*
* port: the port to connect to
* key: the key to retrieve or update
* value: the value to update the supplied key to
*/
int handle_connection(char *port, char *key, char *value);
/* is_valid_key()
* --------------
* Checks if the given key is valid according to the spec
*
* key: the key to check if valid
*
* Returns: 0 if not valid, 1 if valid
*/
int is_valid_key(char *key);
#endif

443
dbserver.c Normal file
View File

@@ -0,0 +1,443 @@
#include "dbserver.h"
int get_nth_char_position(const char *s, int c, int n) {
int charCount = 0;
for (int i = 0; i < strlen(s); i++) {
if (s[i] == c && (++charCount) == n) {
return i;
}
}
return -1;
}
int get_valid_number(char *rawNumber) {
if (strlen(rawNumber) == 0) {
return -1;
}
for (int i = 0; i < strlen(rawNumber); i++) {
if (!isdigit(rawNumber[i])) {
return -1;
}
}
return atoi(rawNumber);
}
char *get_authkey_from_file(char *filePath) {
FILE *fin = NULL;
if ((fin = fopen(filePath, "r")) == NULL) {
return NULL;
}
char *line = NULL;
if ((line = read_line(fin)) == NULL) {
return NULL;
}
fclose(fin);
return line;
}
struct addrinfo *get_address_info(int port) {
struct addrinfo *ai = 0;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
// Convert port to string for use in bind
char portnum[6];
sprintf(portnum, "%d", port);
// Get the address info
int err;
if ((err = getaddrinfo(NULL, portnum, &hints, &ai))) {
freeaddrinfo(ai);
return NULL;
}
return ai;
}
int open_socket(int port) {
struct addrinfo *ai = 0;
if ((ai = get_address_info(port)) == NULL) {
return -1;
}
// Bind socket to port
int recvFd = socket(AF_INET, SOCK_STREAM, 0);
// Reuse ports immediately
int optionValue = 1;
// clang-format off
if (setsockopt(recvFd, SOL_SOCKET, SO_REUSEADDR, &optionValue,
sizeof(int)) < 0) {
// clang-format on
freeaddrinfo(ai);
return -1;
}
// Bind fd to socket
if (bind(recvFd, (struct sockaddr *)ai->ai_addr, sizeof(struct sockaddr)) <
0) {
freeaddrinfo(ai);
return -1;
}
freeaddrinfo(ai);
// Backlogged if not being used right away
if (listen(recvFd, MAX_BACKLOG) < 0) {
return -1;
}
// Get port number and print to stdout
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (getsockname(recvFd, (struct sockaddr *)&sin, &len) == -1) {
return -1;
} else {
fprintf(stderr, "%d\n", ntohs(sin.sin_port));
}
return recvFd;
}
void serverstats_init() {
serverStats =
(struct ServerStatistics *)malloc(sizeof(struct ServerStatistics));
serverStats->currentConnections = 0;
serverStats->completedConnections = 0;
serverStats->authFails = 0;
serverStats->gets = 0;
serverStats->puts = 0;
serverStats->deletes = 0;
serverStats->connectionLock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
}
int handle_listen(int port, int connections, char *authKey) {
// Create a public (0) and private (1) stringstore
StringStore *stringStores[2] = {NULL, NULL};
stringStores[0] = stringstore_init();
stringStores[1] = stringstore_init();
struct sockaddr_in fromAddress = {0};
socklen_t fromAddressSize = sizeof(fromAddress);
int recvFd;
if ((recvFd = open_socket(port)) < 0) {
return EXIT_PORT_ERR;
}
while (1) {
int fd = accept(
recvFd, (struct sockaddr *)&fromAddress, &fromAddressSize);
if (fd < 0) {
return 1;
}
char hostname[NI_MAXHOST];
int err;
// clang-format off
if ((err = getnameinfo((struct sockaddr *)&fromAddress,
fromAddressSize, hostname, NI_MAXHOST, NULL, 0, 0))) {
// clang-format on
return 1;
}
struct ConnectionInfo ci;
memset(&ci, 0, sizeof(struct ConnectionInfo));
ci.fd = fd;
ci.connections = connections;
ci.authKey = authKey;
ci.stringStores = stringStores;
struct ConnectionInfo *ciPtr = malloc(sizeof(struct ConnectionInfo));
*ciPtr = ci;
pthread_t threadId;
pthread_create(&threadId, NULL, handle_connection, ciPtr);
pthread_detach(threadId);
}
free(stringStores[0]);
free(stringStores[1]);
close(recvFd);
return 0;
}
char *process_response_get(char *key, StringStore *stringStore) {
char *response = NULL;
char *value = NULL;
if ((value = (char *)stringstore_retrieve(stringStore, key)) == NULL) {
response = construct_HTTP_response(404, "Not Found", NULL, NULL);
} else {
response = construct_HTTP_response(200, "OK", NULL, value);
pthread_mutex_lock(&(serverStats->connectionLock));
serverStats->gets += 1;
pthread_mutex_unlock(&(serverStats->connectionLock));
}
return response;
}
char *process_response_put(char *key, char *value, StringStore *stringStore) {
char *response = NULL;
if (stringstore_add(stringStore, key, value) == 0) {
response = construct_HTTP_response(
500, "Internal Server Error", NULL, NULL);
} else {
response = construct_HTTP_response(200, "OK", NULL, NULL);
pthread_mutex_lock(&(serverStats->connectionLock));
serverStats->puts += 1;
pthread_mutex_unlock(&(serverStats->connectionLock));
}
return response;
}
char *process_response_delete(char *key, StringStore *stringStore) {
char *response = NULL;
if (stringstore_delete(stringStore, key) == 0) {
response = construct_HTTP_response(404, "Not Found", NULL, NULL);
} else {
response = construct_HTTP_response(200, "OK", NULL, NULL);
pthread_mutex_lock(&(serverStats->connectionLock));
serverStats->deletes += 1;
pthread_mutex_unlock(&(serverStats->connectionLock));
}
return response;
}
int check_auth_key(HttpHeader **headers, char *authKey) {
for (int i = 0; headers[i] != NULL; i++) {
if (strcmp(headers[i]->name, "Authorization") == 0 &&
strcmp(headers[i]->value, authKey) == 0) {
return 1;
}
}
return 0;
}
char *construct_response(
struct RequestParams req, StringStore **stringStores) {
char *response = NULL;
// Default to public string store
StringStore *stringStore = stringStores[0];
if (req.path == NULL || req.key == NULL) {
return NULL;
}
// If the client is trying to access a private path
if (strcmp(req.path, PRIVATE_PATH) == 0) {
if (!check_auth_key(req.headers, req.authKey)) {
// Client failed to auth
pthread_mutex_lock(&(serverStats->connectionLock));
serverStats->authFails += 1;
pthread_mutex_unlock(&(serverStats->connectionLock));
return construct_HTTP_response(401, "Unauthorized", NULL, NULL);
} else {
stringStore = stringStores[1];
}
} else if (strcmp(req.path, PUBLIC_PATH) != 0) {
return NULL;
}
if (strcmp(req.method, "GET") == 0) {
response = process_response_get(req.key, stringStore);
} else if (strcmp(req.method, "PUT") == 0) {
response = process_response_put(req.key, req.value, stringStore);
} else if (strcmp(req.method, "DELETE") == 0) {
response = process_response_delete(req.key, stringStore);
}
return response;
}
int get_path_key(char *address, char **path, char **key) {
int keyStartPos = get_nth_char_position(address, '/', 2) + 1;
if (keyStartPos <= 0) {
return 1;
}
*path = (char *)malloc(sizeof(char) * (keyStartPos + 1));
strncpy(*path, address, keyStartPos);
*key = (char *)malloc(
sizeof(char) * ((strlen(address) - keyStartPos) + 1));
strcpy(*key, &address[keyStartPos]);
return 0;
}
struct RequestParams request_params_init() {
struct RequestParams requestParams = {0};
memset(&requestParams, 0, sizeof(struct RequestParams));
requestParams.method = NULL;
requestParams.path = NULL;
requestParams.key = NULL;
requestParams.value = NULL;
requestParams.authKey = NULL;
requestParams.headers = NULL;
memset(&(requestParams.headers), 0, sizeof(HttpHeader *));
return requestParams;
}
int handle_connection_request(
FILE *recv, FILE *send, char *authKey, StringStore **stringStores) {
struct RequestParams req = request_params_init();
char *address = NULL;
// clang-format off
if (get_HTTP_request(recv, &(req.method), &address, &(req.headers),
&(req.value)) == 1) {
// clang-format on
get_path_key(address, &(req.path), &(req.key));
req.authKey = authKey;
char *response = NULL;
if ((response = construct_response(req, stringStores)) == NULL) {
response = construct_HTTP_response(400, "Bad Request", NULL, NULL);
}
fprintf(send, response);
fflush(send);
// Will only need to free in this branch
free(req.method);
free(address);
free(req.value);
free(response);
free(req.path);
free(req.key);
free_array_of_headers(req.headers);
return 0;
}
return 1;
}
void handle_client_connection(
int sendFd, char *authKey, StringStore **stringStores) {
int recvFd = dup(sendFd);
FILE *send = fdopen(sendFd, "w");
FILE *recv = fdopen(recvFd, "r");
// Loop until EOF or invalid response
int err = 0;
// clang-format off
while ((err = handle_connection_request(
recv, send, authKey, stringStores)) == 0) {
// clang-format on
;
}
fclose(recv);
fclose(send);
close(recvFd);
}
void *handle_connection(void *ciPtr) {
struct ConnectionInfo ci = *(struct ConnectionInfo *)ciPtr;
free(ciPtr);
// Lock connection count variable while updating
pthread_mutex_lock(&(serverStats->connectionLock));
serverStats->currentConnections += 1;
pthread_mutex_unlock(&(serverStats->connectionLock));
int illegalConnection = 0;
if (serverStats->currentConnections <= ci.connections ||
ci.connections == 0) {
// if within the max connections
handle_client_connection(ci.fd, ci.authKey, ci.stringStores);
} else {
// send them away
write(ci.fd, UNAVALIABLE_RESPONSE, strlen(UNAVALIABLE_RESPONSE));
illegalConnection = 1;
}
close(ci.fd);
pthread_mutex_lock(&(serverStats->connectionLock));
serverStats->currentConnections--;
if (!illegalConnection) {
serverStats->completedConnections++;
}
pthread_mutex_unlock(&(serverStats->connectionLock));
return NULL;
}
void print_server_stats(int s) {
fprintf(stderr,
"Connected clients:%d\nCompleted clients:%d\nAuth "
"failures:%d\nGET "
"operations:%d\nPUT operations:%d\nDELETE operations:%d\n",
serverStats->currentConnections, serverStats->completedConnections,
serverStats->authFails, serverStats->gets, serverStats->puts,
serverStats->deletes);
}
void setup_signal_handler(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = print_server_stats;
sa.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sa, 0);
}
int main(int argc, char *argv[]) {
serverstats_init();
setup_signal_handler();
if (argc < 3 || argc > 4) {
fprintf(stderr, PROGRAM_USAGE);
exit(EXIT_ERR);
}
int connections = 0;
if ((connections = get_valid_number(argv[2])) < 0) {
fprintf(stderr, PROGRAM_USAGE);
exit(EXIT_ERR);
}
int portnum = 0;
if (argv[3] && strlen(argv[3]) > 0) {
portnum = get_valid_number(argv[3]);
if (portnum != 0 && (portnum < PORT_MIN || portnum > PORT_MAX)) {
fprintf(stderr, PROGRAM_USAGE);
exit(EXIT_ERR);
}
}
char *authKey = get_authkey_from_file(argv[1]);
if (authKey == NULL) {
fprintf(stderr, "dbserver: unable to read authentication string\n");
exit(EXIT_FILE_ERR);
}
int err;
if ((err = handle_listen(portnum, connections, authKey)) > 0) {
fprintf(stderr, "dbserver: unable to open socket for listening\n");
free(authKey);
exit(err);
}
free(authKey);
free(serverStats);
}

270
dbserver.h Normal file
View File

@@ -0,0 +1,270 @@
#ifndef DBSERVER_H
#define DBSERVER_H
#include <csse2310a3.h>
#include <csse2310a4.h>
#include <ctype.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stringstore.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define PORT_MIN 1024
#define PORT_MAX 65535
#define MAX_BACKLOG 10
#define UNAVALIABLE_RESPONSE \
"HTTP/1.1 503 Service Unavaliable\r\nContent-Length: 0\r\n\r\n"
#define OK_RESPONSE "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
#define ISR_RESPONSE \
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"
#define PROGRAM_USAGE "Usage: dbserver authfile connections [portnum]\n"
#define PUBLIC_PATH "/public/"
#define PRIVATE_PATH "/private/"
// Program exit codes
enum ExitCodes {
EXIT_OK = 0,
EXIT_ERR = 1,
EXIT_FILE_ERR = 2,
EXIT_PORT_ERR = 3
};
// Server Statistics
struct ServerStatistics {
int currentConnections;
int completedConnections;
int authFails;
int gets;
int puts;
int deletes;
pthread_mutex_t connectionLock;
};
// Connection information needed for threads
struct ConnectionInfo {
int fd;
int connections;
char *authKey;
struct ConnectionThread *connectionThread;
StringStore **stringStores;
};
// Information regarding a client request
struct RequestParams {
char *method;
char *path;
char *key;
char *value;
char *authKey;
HttpHeader **headers;
};
// The server statistic instance, global as needed to be called by SIGHUP
struct ServerStatistics *serverStats;
/* get_nth_char_position()
* -----------------------
* Get the character position of the nth occurrence of that character
*
* s: the string to search
* c: the character to look for
* n: the nth occurance to retrieve the position of
*
* Returns: the position of the nth ocurrance of the given character
*/
int get_nth_char_position(const char *s, int c, int n);
/* get_valid_number()
* ------------------
* Checks if a given number is an actual number, then returns it as an int
*
* rawNumber: the number to check and convert
*
* Returns: -1 if the number is not valid, the converted string if it is
* valid
*/
int get_valid_number(char *rawNumber);
/* get_authkey_from_file()
* -----------------------
* Gets the first line from a file
*
* filePath: the path to the file
*
* Returns: NULL if the file does not exist or the first line does not
* exist, the first line otherwise
*/
char *get_authkey_from_file(char *filePath);
/* get_address_info()
* ------------------
* Gets the required addrinfo for the given port
*
* port: the port to assign to
*
* Returns: the appropriate addrinfo
*/
struct addrinfo *get_address_info(int port);
/* open_socket()
* -------------
* Opens a socket for the given port holding the given connections
*
* port: the port to open a socket on
* connections: the amount of connections to support
*
* Returns: -1 if failed to connect to socket, 0 if success
*/
int open_socket(int port);
/* serverstats_init()
* -----------------------
* Initializes the server statistics
*
* Returns: the newly initialised server stats
*/
void serverstats_init(void);
/* handle_listen()
* ---------------
* Opens a socket and deals with connections
*
* port: the port to open a socket on
* connections: the amount of connections to support
* authKey: the authorization key for the private db
*
* Returns: 3 if failed to connect to socket, 0 if success
*/
int handle_listen(int port, int connections, char *authKey);
/* process_response_get()
* -----------------------
* Gets the appropriate get response for the request from the client
*
* key: the key to get the value of from the db
* stringStore: the db
*
* Returns: the appropriate response (200 with value or 404)
*/
char *process_response_get(char *key, StringStore *stringStore);
/* process_response_put()
* -----------------------
* Adds the appropriate value to the db and then gets the appropriate
* response
*
* key: the key to update the value of in the db
* value: the value to update the key with
* stringStore: the db
*
* Returns: the appropiate response (200 or 500 if failed)
*/
char *process_response_put(char *key, char *value, StringStore *stringStore);
/* process_response_delete()
* -----------------------
* Deletes the given key from the db and returns the appropriate response
*
* key: the key to delete from the db
* stringStore: the db
*
* Returns: the appropriate response (200 or 404)
*/
char *process_response_delete(char *key, StringStore *stringStore);
/* check_auth_key()
* ----------------
* Check the given authentication key with the given headers
*
* headers: the headers that may contain the authorization header
* authKey: the auth key to check against the header
*
* Returns: 1 if the key matches, 0 otherwise
*/
int check_auth_key(HttpHeader **headers, char *authKey);
/* construct_response()
* --------------------
* Process the request and construct the needed response
*
* req: the request parameters
* stringStores: the db to update keys
*
* Returns: the required response
*/
char *construct_response(struct RequestParams req, StringStore **stringStores);
/* get_path_key()
* --------------
* Gets the needed path and key from the supplied address
*
* address: the address given by the HTTP request
* path: the pointer to the path variable to update
* key: the pointer to the key variable to update
*/
int get_path_key(char *address, char **path, char **key);
/* request_params_init()
* ---------------------
* Initialise the requestParams struct
*
* Returns: the initialised requestParams struct
*/
struct RequestParams request_params_init(void);
/* handle_connection_request()
* ---------------------------
* Handles the clients request
*
* recv: the connection file to read from
* send: the connection file to write to
* authKey: the authorization key for the private db
* stringStores: the arrays of dbs
*/
int handle_connection_request(
FILE *recv, FILE *send, char *authKey, StringStore **stringStores);
/* handle_client_connection()
* ---------------------------
* Handles the connection through the given fd
*
* fd: the fd to send and recieve information for the connection on
* authKey: the authorization key for the private db
* stringStores: the array of dbs
*/
void handle_client_connection(
int fd, char *authKey, StringStore **stringStores);
/* handle_connection()
* -------------------
* Thread function for handling connections to the server, if there are too
* many connections the server will turn away the client
*
* ciPtr: the pointer to the connection information
*/
void *handle_connection(void *ciPtr);
/* print_server_stats()
* --------------------
* Prints the server statistics. used by SIGHUP handler
*
* s: the signal from the signal handler
*/
void print_server_stats(int s);
/* setup_signal_handler()
* ----------------------
* Sets up the signal handler for SIGHUP
*/
void setup_signal_handler(void);
#endif

72
stringstore.c Normal file
View File

@@ -0,0 +1,72 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stringstore.h>
typedef struct StringStoreContents {
char *key;
char *value;
} StringStoreContents;
struct StringStore {
struct StringStoreContents *contents;
int size;
};
StringStore *stringstore_init(void) {
StringStore *stringStore = (StringStore *)malloc(sizeof(StringStore));
memset(stringStore, 0, sizeof(StringStore));
stringStore->size = 0;
stringStore->contents =
(StringStoreContents *)malloc(sizeof(StringStoreContents));
return stringStore;
}
StringStore *stringstore_free(StringStore *store) {
for (int i = 0; i < store->size; i++) {
free(store->contents[i].key);
free(store->contents[i].value);
}
free(store->contents);
free(store);
return NULL;
}
int stringstore_add(StringStore *store, const char *key, const char *value) {
stringstore_delete(store, key);
store->contents = (StringStoreContents *)realloc(
store->contents, sizeof(StringStoreContents) * (store->size + 1));
if ((store->contents[store->size].key = strdup(key)) == NULL ||
(store->contents[store->size].value = strdup(value)) == NULL) {
return 0;
}
store->size++;
return 1;
}
const char *stringstore_retrieve(StringStore *store, const char *key) {
for (int i = 0; i < store->size; i++) {
if (strcmp(store->contents[i].key, key) == 0) {
return store->contents[i].value;
}
}
return NULL;
}
int stringstore_delete(StringStore *store, const char *key) {
for (int i = 0; i < store->size; i++) {
if (strcmp(store->contents[i].key, key) == 0) {
free(store->contents[i].key);
free(store->contents[i].value);
if ((i + 1) < store->size) {
store->contents[i] = store->contents[i + 1];
}
store->size--;
return 1;
}
}
return 0;
}