I have a web server that can send websites, images, mp3 and other things and I was wondering how I could improve the code and make the server more efficient.
//this is where you enter the default file directories
char *defaultDir1 = "/Users/rowan/Desktop/webServer(Mac)/website/displaySite/";
char *defaultDir2 = "/Users/rowan/Desktop/webServer(Mac)/";
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <pthread.h>
struct sockaddr_in sockaddr;
char webFileDir[9000];
char servFileDir[9000];
#define THREAD_POOL_SIZE 200
#define BACKLOG 200
pthread_t thread_pool[THREAD_POOL_SIZE];
//creates a lock and unlock system for enqueue and dequeue
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//creates a sleep timer for the threads/does something until given a signal or condition.
pthread_cond_t condition_var = PTHREAD_COND_INITIALIZER;
struct node {
int client_socket;
struct node *next;
};
typedef struct node node_t;
node_t *head = NULL;
node_t *tail = NULL;
void enqueue(int client_socket) {
node_t *newnode = malloc(sizeof(node_t)); //create node
newnode->client_socket = client_socket; //assign client socket
newnode->next = NULL; //add node to the end of the list
if (tail == NULL) { //checks if there is nothing on the tail and if so adds a head
head = newnode;
} else {
//if there is a tail then add a new node onto the tail
tail->next = newnode;
}
tail = newnode;
//this all basically creates a new node then moves it over over and over again until stopped
}
int dequeue() {
if (head == NULL) return -1;//if the head is empty or nothing has been enqueued then return null
if (head != NULL) { //else...
int result = head->client_socket;
node_t *temp = head;
head = head->next;
if (head == NULL) {tail = NULL;}
free(temp);
return result;
}
//this function removes everything off of the queue and returns the socket
return 0;
}
int *handleClient(int pclientSock) {
FILE *fpointer;
char *findExtention;
char *clientIP;
char *endDir;
int freadErr;
int fsize;
int bin;
int readSock;
char recvLine[2];
char fileDir[9000];
char recvLineGET[60];
char httpResponseReport[1000];
char *fileLine;
char httpResponse[1000];
char *endOfPost;
char fullResp[100000];
int checkSend;
char *startDir;
char *getEnd;
int responseCode = 200;
bzero(fileDir, sizeof(fileDir));
bzero(recvLineGET, sizeof(recvLineGET));
//get client ip
int acceptSock = pclientSock;
socklen_t addrSize = sizeof(struct sockaddr_in);
getpeername(acceptSock, (struct sockaddr *)&sockaddr, &addrSize);
clientIP = inet_ntoa(sockaddr.sin_addr);
fullResp[0] = '\0';
//handles reading client http request
while (1) {
if ((readSock = read(acceptSock, recvLine, 1)) != 1) {
perror("error reading from socket");
//error 500: internal server error
send(acceptSock, "HTTP/1.1 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(recvLine, sizeof(recvLine));
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
recvLine[1] = '\0';
strcat(fullResp, recvLine);
if ((endOfPost = strstr(fullResp, "\n")) != NULL) {
break;
} else if (readSock < 0) {
//perror("endOfHttpBody error");
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
bzero(recvLine, sizeof(recvLine));
}
strcpy(fileDir, webFileDir);
if ((getEnd = strstr(fullResp, "HTTP")) == NULL) {
perror("error reading from socket");
bzero(fullResp, sizeof(fullResp));
//error 400: bad request
send(acceptSock, "HTTP/1.0 400\r\n\r\n", 16, MSG_NOSIGNAL);
close(acceptSock);
return 0;
}
strcpy(getEnd, "");
if ((startDir = strchr(fullResp, '/')) == NULL) {
perror("this shouldnt happen .-.");
printf("startDir: %s\n", startDir);
//error 400: bad request
send(acceptSock, "HTTP/1.0 400\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
//handles the file retrieving
if ((endDir = strchr(startDir, ' ')) == NULL) {
perror("endDir error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fileDir, sizeof(fileDir));
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
strcpy(endDir, "");
//checks for requested directory
if (strcmp(startDir, "/") == 0) {
findExtention = ".html";
strcpy(fileDir, webFileDir);
strcat(fileDir, "index.html");
responseCode = 200;
} else {
if ((findExtention = strchr(startDir, '.')) == NULL) {
perror("invalid webpage");
findExtention = ".html";
strcpy(fileDir, servFileDir);
strcat(fileDir, "err404.html");
responseCode = 404;
}
strcat(fileDir, startDir);
}
if ((fpointer = fopen(fileDir, "rb")) != NULL) {
fseek(fpointer, 0L, SEEK_END);
fsize = ftell(fpointer);
} else if (strcmp(startDir-1, "/favicon.ico") == 0 && access(fileDir, F_OK) != 0) {
strcpy(fileDir, servFileDir);
strcat(fileDir, "favicon.ico");
if (access(fileDir, F_OK) != 0) {
printf("\n\n\nERROR: please do not delete the default favicon.ico file. the program will not work properly if it is deleted\n\n\n");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
exit(1);
}
if ((fpointer = fopen(fileDir, "rb")) == NULL) {
perror("fopen error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
fseek(fpointer, 0L, SEEK_END);
fsize = ftell(fpointer);
} else if (access(fileDir, F_OK) != 0) {
perror("webpage doesnt exist");
findExtention = ".html";
strcpy(fileDir, servFileDir);
strcat(fileDir, "err404.html");
responseCode = 404;
if ((fpointer = fopen(fileDir, "r")) == NULL) {
perror("fopen error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
fseek(fpointer, 0L, SEEK_END);
fsize = ftell(fpointer);
}
fclose(fpointer);
//sets the server http response
if ((strcmp(findExtention, ".jpeg")) == 0 || (strcmp(findExtention, ".jpg")) == 0) {
bin = 1;
sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: image/jpeg\r\nContent-Length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
} else if ((strcmp(findExtention, ".png")) == 0) {
bin = 1;
sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: image/png\r\nContent-Length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
} else if ((strcmp(findExtention, ".ico")) == 0) {
bin = 1;
sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: image/x-icon\r\nContent-Length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
} else if ((strcmp(findExtention, ".mp3")) == 0) {
bin = 1;
sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: audio/mpeg\r\nContent-length: %d\r\nContent-Transfer-Encoding: binary\r\n\r\n", responseCode, fsize);
} else if ((strcmp(findExtention, ".html")) == 0) {
bin = 0;
sprintf(httpResponse, "HTTP/1.0 %d\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", responseCode, fsize);
} else {
strcpy(fileDir, "err404.html");
if ((fpointer = fopen(fileDir, "r")) == NULL) {
perror("fopen error");
bzero(fullResp, sizeof(fullResp));
close(acceptSock);
return 0;
}
fseek(fpointer, 0L, SEEK_END);
fsize = ftell(fpointer);
fclose(fpointer);
bin = 0;
sprintf(httpResponse, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: %d\r\n\r\n", fsize);
}
strcpy(httpResponseReport, httpResponse);
if (readSock < 0) {
perror("readsock is less than 0");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
bzero(fileDir, sizeof(fileDir));
bzero(httpResponseReport, sizeof(httpResponseReport));
bzero(httpResponse, sizeof(httpResponse));
close(acceptSock);
return 0;
}
//checks if i need to read plaintext or binary
if (bin == 0) {
fpointer = fopen(fileDir, "r");
} else if (bin == 1) {
fpointer = fopen(fileDir, "rb");
}
if (fpointer == NULL) {
perror("file open error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
bzero(fileDir, sizeof(fileDir));
bzero(httpResponseReport, sizeof(httpResponseReport));
bzero(httpResponse, sizeof(httpResponse));
close(acceptSock);
return 0;
}
//sends server http response to client
fseek(fpointer, 0L, SEEK_END);
fsize = ftell(fpointer);
fclose(fpointer);
//checks if i need to read plaintext or binary (again)
if (bin == 0) {
fpointer = fopen(fileDir, "r");
} else if (bin == 1) {
fpointer = fopen(fileDir, "rb");
}
if (fpointer == NULL) {
perror("fopen error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
bzero(fileDir, sizeof(fileDir));
bzero(httpResponse, sizeof(httpResponse));
bzero(httpResponseReport, sizeof(httpResponseReport));
close(acceptSock);
return 0;
}
fileLine = malloc(fsize * sizeof(char));
while ((freadErr = fread(fileLine, fsize, fsize, fpointer)) > 0);
if (feof(fpointer) < 0) {
perror("fread error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
bzero(fullResp, sizeof(fullResp));
bzero(fileDir, sizeof(fileDir));
bzero(httpResponseReport, sizeof(httpResponseReport));
bzero(httpResponse, sizeof(httpResponse));
close(acceptSock);
return 0;
}
//set 5 second socket timeout
struct timeval timeout;
timeout.tv_sec = 15;
timeout.tv_usec = 0;
if (setsockopt(acceptSock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("setsockopt error");
//error 500: internal server error
send(acceptSock, "HTTP/1.0 500\r\n\r\n", 16, MSG_NOSIGNAL);
close(acceptSock);
return 0;
}
while (1) {
if ((checkSend = send(acceptSock, httpResponse, strlen(httpResponse), MSG_NOSIGNAL)) == -1) {
break;
}
//send full response
if ((checkSend = send(acceptSock, fileLine, fsize, MSG_NOSIGNAL)) == -1) {
break;
}
sleep(1);
}
bzero(httpResponse, sizeof(httpResponse));
bzero(fullResp, sizeof(fullResp));
fclose(fpointer);
close(acceptSock);
free(fileLine);
//handles the clearing of variables and logs client ip and requested directory
printf("\nclient with the ip: %s has requested %s.\n", clientIP, fileDir);//added server response but i don't think i need it anymore
bzero(fileDir, sizeof(fileDir));
bzero(httpResponseReport, sizeof(httpResponseReport));
return 0;
}
//assign work to each thread
void *giveThreadWork() {
while (1) {
int pclient;
pthread_mutex_lock(&mutex);
//makes thread wait until signaled
while ((pclient = dequeue()) == -1) {
pthread_cond_wait(&condition_var, &mutex);
}
pthread_mutex_unlock(&mutex);
handleClient(pclient);
}
}
int main() {
int clientSock;
int sock;
int portNum;
int defaultOrNo;
printf("\n\n\nWeb Server\nBy: Rowan Rothe\n\n\n");
printf("would you like to use default directories (1 = yes, 0 = no): ");
scanf("%d", &defaultOrNo);
if (defaultOrNo == 0) {
printf("enter the directory of the files to be served (with '/' at the end): ");
scanf("%s", webFileDir);
printf("enter the directory of the web server folder (with '/' at the end): ");
scanf("%s", servFileDir);
} else if (defaultOrNo == 1) {
strcpy(webFileDir, defaultDir1);
strcpy(servFileDir, defaultDir2);
}
printf("what port would you like to host the site on?: ");
scanf("%d", &portNum);
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("sock error");
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockaddr.sin_port = htons(portNum);
if ((bind(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) < 0) {
perror("bind error");
exit(1);
}
printf("socket bind success\n");
//create all the threads for the thread pool
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
pthread_create(&thread_pool[i], NULL, giveThreadWork, NULL);
}
printf("created thread pool of size %d successfully\n", THREAD_POOL_SIZE);
if ((listen(sock, BACKLOG)) < 0) {
perror("listen error");
}
printf("listen success\n");
while (1) {
//checks for client connection
if ((clientSock = accept(sock, NULL, NULL)) < 0) {
perror("accept error");
} else {
pthread_mutex_lock(&mutex);
enqueue(clientSock);
pthread_cond_signal(&condition_var);
pthread_mutex_unlock(&mutex);
}
}
return 0;
}