first commit

This commit is contained in:
Jose Caban
2025-06-07 01:59:34 -04:00
commit 388ac241f0
3558 changed files with 9116289 additions and 0 deletions

View File

@@ -0,0 +1 @@
obj

View File

@@ -0,0 +1,10 @@
/.cvsignore/1.1/Tue Jan 24 02:28:52 2006//
/Makefile/1.8/Sat Feb 18 02:10:09 2006//
/boss.c/1.11/Mon Feb 20 23:05:47 2006//
/boss.h/1.4/Thu Feb 16 01:13:54 2006//
/client.c/1.27/Mon Feb 20 23:05:47 2006//
/client.h/1.4/Thu Feb 16 01:13:54 2006//
/config.c/1.11/Mon Feb 20 16:00:58 2006//
/config.h/1.7/Sat Feb 18 23:49:55 2006//
/main.c/1.15/Sat Feb 18 23:49:55 2006//
D

View File

@@ -0,0 +1 @@
cs4210/proj1/src/server

View File

@@ -0,0 +1 @@
/usr/_CVS

View File

@@ -0,0 +1,106 @@
##############################################################################
#
# Generic Makefile. Only need to modify the variables for src, obj,
# and bin directories, and the name of the executable.
#
# $Author: vurazov $
# $Date: 2006/02/18 02:10:09 $
# $Revision: 1.8 $
#
##############################################################################
########################### Directories and Target ###########################
# Source directory:
SRC_DIR = .
# Object directory:
OBJ_DIR = ./obj
# Executable directory:
BIN_DIR = ../../bin
# The static libraries to link with the code:
STATIC_LIBS = ../../bin/libcommon.a
# Name of the executable:
BIN_NAME = server
######################## Compiler and Linker Options #########################
# Compiler:
CC = gcc
# Linker:
LD = gcc
# Preprocessor flags:
DFLAGS =
# Compiler flags:
CFLAGS = -std=c99 -Wall -pedantic -O2 -I..
# Linker flags:
LDFLAGS = -lpthread #-lsocket
############################ Other Programs Used #############################
# Dependency generator:
MDEPEND = $(CC) -M -I..
# Make Dir command:
MKDIR = /bin/mkdir -p
# Clean-up command:
RM = /bin/rm -f
######################### Automatic Object Variables #########################
# The list of source files:
SRCS = $(wildcard $(SRC_DIR)/*.c)
# Generated object files:
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
OBDS = $(patsubst $(SRC_DIR)/%.c,%.o,$(SRCS))
# Look for .o files in obj dir:
vpath %.o $(OBJ_DIR)
# Program file:
PROG = $(BIN_DIR)/$(BIN_NAME)
################################### Rules ####################################
# Top-level rule: compile everything
all: $(PROG)
# The program link rule:
$(PROG): $(OBDS) $(BIN_DIR)
$(LD) $(LDFLAGS) -o $(PROG) $(OBJS) $(STATIC_LIBS)
# Meta rule for compiling ".c" files
%.o: $(SRC_DIR)/%.c $(OBJ_DIR)
$(CC) $(CFLAGS) $(DFLAGS) -c -o $(OBJ_DIR)/$@ $<
# Rules for obj and bin dirs:
$(OBJ_DIR):
$(MKDIR) $(OBJ_DIR)
$(BIN_DIR):
$(MKDIR) $(BIN_DIR)
# Rule for cleaning up before a recompile:
.PHONY: clean
clean:
$(RM) $(PROG) $(OBJS) .depend
# Rule for creating dependency lists and writing them into a dependency file:
.depend: $(SRCS)
$(MDEPEND) $(SRCS) > .depend
#Include dependency list:
include .depend

View File

@@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdlib.h>
#include "common/debug.h"
#include "common/queue.h"
#include "common/threading.h"
#include "boss.h"
#include "client.h"
/* The id of the listening socket. */
static socket_t m_listening_socket = -1;
/* Child count */
static int child_count = 0;
/* List of child threads. */
static pthread_t* m_children = NULL;
/* The queue of client sockets. */
static queue_t m_client_sockets;
int boss_initialize(port_t pnum, int thread_count)
{
int index = 0;
int result = 0;
#ifdef WIN32
/* Have to also initialize WinSock */
WSADATA uselessData;
result = WSAStartup(MAKEWORD(2, 2), &uselessData);
if (result)
{
fprintf(stderr, "[BOSS] Could not initialize WinSock.\n");
net_report_error();
return result;
}
#endif
child_count = thread_count;
/* Make a listening socket: */
m_listening_socket = net_listen_on_port(pnum);
if (IS_BAD_SOCKET(m_listening_socket))
{
return 1;
}
/* Initialize the queue */
queue_initialize(&m_client_sockets);
/* Spawn children threads. */
m_children = (pthread_t*) calloc(thread_count, sizeof(pthread_t));
if (!m_children)
{
fprintf(stderr, "[BOSS] Could not allocate memory.\n");
net_report_error();
return 1;
}
for (index = 0; index < thread_count; index++)
{
result = pthread_create(m_children + index, NULL,
client_run, &m_client_sockets);
if (result)
{
fprintf(stderr, "[BOSS] Could not create child thread.\n");
net_report_error();
return result;
}
}
return 0;
}
void* boss_run(void* param)
{
/* Loop infinitely accepting connections: */
while (1)
{
/* Accept connection */
socket_t client_socket = -1;
sockaddress_t caddr;
socklen_t sizeof_caddr = sizeof(sockaddress_t);
DEBUG_PRINTF(("[BOSS] Waiting for a client connection...\n"));
client_socket = accept(m_listening_socket,
(struct sockaddr*) &caddr, &sizeof_caddr);
if (IS_BAD_SOCKET(client_socket))
{
fprintf(stderr,
"[BOSS] Could not accept client on listening socket\n");
net_report_error();
continue;
}
DEBUG_PRINTF(("[BOSS] Got client on port %d.\n", ntohs(caddr.sin_port)));
/* Enqueue socket to be picked up by a client thread. */
queue_enqueue(&m_client_sockets, (void*) client_socket);
}
return NULL;
}
int boss_clean()
{
/* Kill children */
int index;
for (index = 0; index < child_count; index++)
{
pthread_kill(m_children[index], SIGINT);
}
DEBUG_PRINTF(("[BOSS] Shutting down...\n\n"));
return net_close_socket(m_listening_socket);
}

View File

@@ -0,0 +1,31 @@
#ifndef _BOSS_H_
#define _BOSS_H_
#include "common/networking.h"
/*
* Initializes the boss thread. pnum is the port number on which we
* would like to start listening to connections. Returns zero upon
* success and various numbers on failures. The thread_count parameter
* is the number of child threads to spawn for handling requests.
*/
int boss_initialize(port_t pnum, int thread_count);
/*
* This function loops infinitely and accepts connections, spawning
* worker threads any time somebody wants to connect. Again, the
* function will loop *infinitely*. The thread it runs in needs to be
* killed explicitly. This is because the accept connection function
* is blocking, so there is no way to kill the thread any way, without
* using extreme methods.
*/
void* boss_run(void* param);
/*
* Performs necessary clean-up of the boss thread code after the
* buss_run function has been killed. Namely, closes the listening
* socket, and such.
*/
int boss_clean();
#endif/*_BOSS_H_*/

View File

@@ -0,0 +1,173 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "common/debug.h"
#include "common/http.h"
#include "common/networking.h"
#include "common/queue.h"
#include "common/util.h"
#include "client.h"
#include "config.h"
/* Returns true if the file exists, and false if it does not. */
static int m_file_exists(const char* name)
{
struct stat buf;
int result = !stat(name, &buf);
return result;
}
/* Converts the requested URI to a local file path for the file to
* serve. If the URI is malformed, returns NULL, else returns the
* local file path in a newly allocated string, which needs to be
* freed eventually. */
static char* m_uri_to_local(const char* uri)
{
char* result = NULL;
/* We don't like people who try to go up the directory hierarchy: */
if (strstr(uri, "/.."))
{
return NULL;
}
/* We also don't like people who try to get funky with form parameters: */
if (strstr(uri, "?"))
{
return NULL;
}
/* We also don't like people who provide illegal paths containing
double slashes: */
if (strstr(uri, "//"))
{
return NULL;
}
/* We also don't like people who try funny business with backslashes: */
if (strstr(uri, "\\"))
{
return NULL;
}
/* If we are still here, I suppose we got a well-behaved request. */
result = strcatd(result, config_get_home_dir());
result = strcatd(result, uri);
return result;
}
/* Processes the request. First, parses the header to check what type
* it is, and if it's a GET request, sends out the correct file to the
* client. If any sort of error occurs, will send the correct response
* to the client. */
static void m_process_request(const char* header, socket_t socket)
{
char* req_file = NULL;
char* local_file = NULL;
FILE* input = NULL;
/* Get the request method: */
const char* method = http_get_method(header);
if (strcmp(method, HTTP_METHOD_GET))
{
/* Unsupported method: */
fprintf(stderr, "[CLI] Unsupported method...\n"
" Expected (%p) \"%s\"\n"
" Got (%p) \"%s\"\n",
HTTP_METHOD_GET, HTTP_METHOD_GET, method, method);
http_send_canned_response(socket, HTTP_STATUS_NOTIMPLEMENTED);
return;
}
/* Get the requested file: */
req_file = http_get_requesturi(header);
if (!req_file)
{
/* Could not parse the name out of the header: */
http_send_canned_response(socket, HTTP_STATUS_BADREQUEST);
return;
}
/* Update the file name so it is relative to the document root in
the local system: */
local_file = m_uri_to_local(req_file);
if (!local_file)
{
http_send_canned_response(socket, HTTP_STATUS_BADREQUEST);
free(req_file);
return;
}
/* Check whether the file exists: */
if (!m_file_exists(local_file))
{
http_send_canned_response(socket, HTTP_STATUS_NOTFOUND);
free(req_file);
free(local_file);
return;
}
/* Try to open the file: */
input = fopen(local_file, "r");
if (!input)
{
http_send_canned_response(socket, HTTP_STATUS_FORBIDDEN);
free(req_file);
free(local_file);
return;
}
/* Send the file: */
http_send_header(socket, HTTP_STATUS_OK);
http_send_text_file(socket, input);
/* Clean up: */
free(req_file);
free(local_file);
fclose(input);
}
static void m_handle_client(socket_t csocket)
{
/* Read the header: */
char* header = http_read_header(csocket);
if (!header)
{
DEBUG_PRINTF(("[CLI] Did not get a header...\n"));
return;
}
/* Process the request: */
m_process_request(header, csocket);
/* Free the header string: */
free(header);
/* Clean up the socket: */
DEBUG_PRINTF(("[CLI%d] Shutting down client socket.\n", csocket));
net_close_socket(csocket);
}
void* client_run(void* p)
{
queue_t* q = (queue_t*) p;
while (1)
{
/* This here needs to be done, else we will sometimes crash on
server exit, when the lock in dequeue is killed before the
client thread itself, and hence handle_client was called with
an illegal socket. */
socket_t client = -1;
client = (socket_t) queue_dequeue(q);
if (IS_BAD_SOCKET(client)) break;
else m_handle_client(client);
}
return NULL;
}

View File

@@ -0,0 +1,15 @@
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include "common/threading.h"
/*
* This function will be started in a new thread at the beginning of
* the program and will be passed a queue of client sockets that will
* be populated by the boss thread. This function will spin
* indefinitely consuming the sockets from the queue.
*
*/
void* client_run(void* p);
#endif/*_CLIENT_H_*/

View File

@@ -0,0 +1,96 @@
#include <stdlib.h>
#include "common/util.h"
#include "config.h"
/* Some default values */
#define CONFIG_DVALUE_PORT 1337
#define CONFIG_DVALUE_POOLSIZE 16
#define CONFIG_DVALUE_HOMEDIR "."
/* Some parameter names */
#define CONFIG_PNAME_PORT "port"
#define CONFIG_PNAME_POOLSIZE "pool-size"
#define CONFIG_PNAME_HOMEDIR "home"
/* The port number to run the server on. */
static port_t m_server_port = -1;
/* The number of child threads to create. */
static int m_pool_size = -1;
/* The root directory for the server's files. */
static char* m_home_dir = NULL;
/* Records the parameter. If the name is unknown, prints a warning out
* to the console and usage, and returns an error code. */
int record_parameter(const char* name, const char* value)
{
if (!strcmp(name, CONFIG_PNAME_PORT))
{
/* Got port number: */
m_server_port = atoi(value);
printf("[CONF] Overriding default %s with value %d\n",
CONFIG_PNAME_PORT, m_server_port);
return 0;
}
else if(!strcmp(name, CONFIG_PNAME_POOLSIZE))
{
/* Got pool size: */
m_pool_size = atoi(value);
printf("[CONF] Overriding default %s with value %d\n",
CONFIG_PNAME_POOLSIZE, m_pool_size);
return 0;
}
else if(!strcmp(name, CONFIG_PNAME_HOMEDIR))
{
/* Got home dir: */
free(m_home_dir);
m_home_dir = strdup(value);
printf("[CONF] Overriding default %s with value \"%s\"\n",
CONFIG_PNAME_HOMEDIR, m_home_dir);
return 0;
}
else
{
/* Unknown parameter: */
fprintf(stderr, "[CONF] Unknown parameter \"%s\"\n", name);
config_print_parameters(stderr);
return 1;
}
}
void config_print_parameters(FILE* out)
{
fprintf(out, "Supported parameters:\n");
fprintf(out, "\t%s - the port to listen on. Default value is %d.\n",
CONFIG_PNAME_PORT, CONFIG_DVALUE_PORT);
fprintf(out, "\t%s - the number of threads to pre-create to handle\n"
"\t\tclient requests.\n"
"\t\tDefault value is %d.\n",
CONFIG_PNAME_POOLSIZE, CONFIG_DVALUE_POOLSIZE);
fprintf(out, "\t%s - the root directory for the server's files."
"\n\t\tDefault value is \"%s\".\n",
CONFIG_PNAME_HOMEDIR, CONFIG_DVALUE_HOMEDIR);
fprintf(out, "\t\n");
}
int config_init(int argc, const char** argv)
{
/* Initialize default values: */
m_server_port = CONFIG_DVALUE_PORT;
m_pool_size = CONFIG_DVALUE_POOLSIZE;
m_home_dir = strdup(CONFIG_DVALUE_HOMEDIR);
return parse_command_parameters(argc, argv, record_parameter);
}
void config_free()
{
free(m_home_dir);
}
port_t config_get_port() { return m_server_port; }
int config_get_pool_size() { return m_pool_size; }
const char* config_get_home_dir() { return m_home_dir; }

View File

@@ -0,0 +1,31 @@
#ifndef _SERVER_CONFIG_H_
#define _SERVER_CONFIG_H_
#include <stdio.h>
#include "common/networking.h"
/* Records the parameter with the given name and value. */
int record_parameter(const char* name, const char* value);
/* Initializes the configuration with the parameters specified. */
int config_init(int argc, const char** argv);
/* Cleans up whatever resources the configuration consumed. */
void config_free();
/* Given a stream, will print the list of supported parameters to the
* stream. */
void config_print_parameters(FILE* out);
/* Returns the port to start the server on. */
port_t config_get_port();
/* Returns the number of child threads to spawn for handling client
* requests. */
int config_get_pool_size();
/* Returns the root directory for the server's files. */
const char* config_get_home_dir();
#endif/*_SERVER_CONFIG_H_*/

View File

@@ -0,0 +1,69 @@
#include <stdio.h>
#include <stdlib.h>
#include "common/threading.h"
#include "common/util.h"
#include "boss.h"
#include "config.h"
int main(int argc, const char** argv) {
pthread_t thread_id;
int result;
/* 0. Read configuration parameters: */
if (config_init(argc, argv))
{
return EXIT_FAILURE;
}
/* 1. Initialize the boss: */
result = boss_initialize(config_get_port(), config_get_pool_size());
if (result)
{
config_free();
return EXIT_FAILURE;
}
/* 2. Run the boss thread: */
result = pthread_create(&thread_id, NULL, boss_run, NULL);
if (result)
{
fprintf(stderr, "[MAIN] Could not create boss thread. "
"Error %d.\n\n", result);
boss_clean();
config_free();
return result;
}
printf("[MAIN] The server is now running.\n"
" Hit ENTER key to kill the server.\n");
/* 3. Wait for the user to hit a key in the console to kill the thread: */
result = getchar();
/* 4. If the user hit a key, means we should stop listening. Kill
* the boss thread: */
result = pthread_kill(thread_id, SIGINT);
if (result)
{
fprintf(stderr, "[MAIN] Could not kill boss thread. "
"Error %d.\n\n", result);
}
/* 5. Clean up after ourselves: */
result = boss_clean();
/* 6. Done! */
config_free();
if (result)
{
printf("[MAIN] Program terminated with errors.\n\n");
return EXIT_FAILURE;
}
else
{
printf("[MAIN] Program terminated cleanly.\n\n");
return EXIT_SUCCESS;
}
}