Add initial server functionality and clean up
This commit is contained in:
parent
2f552e5ad4
commit
400c3916ff
@ -15,6 +15,11 @@ the URL. The transmitted content of that file is written to `stdout`, to a file
|
||||
|
||||
## server
|
||||
|
||||
Not implemented yet
|
||||
The server takes a document root as input, reads it and serves it on the port specified(`8080` by default). If the `-i` flag is specified, the server uses its value as an index file. If not, it takes index.html as a default. The server supports **ONLY** GET requests. If it receives another request type it will return a response status **501** (Not implemented). If the file requested is not found, the server returns a response status **404** (Not found).
|
||||
|
||||
SYNOPSIS
|
||||
server [-p PORT] [-i INDEX] DOC_ROOT
|
||||
EXAMPLE
|
||||
server -p 1280 -i index.html ./public/
|
||||
|
||||
**Note: The description is from the task I got from TU.**
|
@ -56,7 +56,6 @@ int main(int argc, char *argv[]) {
|
||||
printf("Command usage:\n");
|
||||
print_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
@ -116,6 +115,8 @@ int main(int argc, char *argv[]) {
|
||||
process_response(reply, ofile, dir);
|
||||
|
||||
freeaddrinfo(config); //< Free the addrinfo struct
|
||||
|
||||
exit(EXIT_SUCCESS); //< Exit with success
|
||||
}
|
||||
|
||||
static void print_usage(void) {
|
||||
@ -177,7 +178,7 @@ static struct addrinfo* get_host_info(const char *hostname, char *port) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to create a socker using the supplied configuration
|
||||
* @brief Function to create a socket using the supplied configuration
|
||||
* @details Creates a socket using the supplied configuration
|
||||
* If there was no error, it returns the socket
|
||||
* If there was an error, it exits and displays an error message
|
||||
@ -185,7 +186,7 @@ static struct addrinfo* get_host_info(const char *hostname, char *port) {
|
||||
* @return sockfd - The socket obtained from the socket creation
|
||||
**/
|
||||
static int create_socket(struct addrinfo* config) {
|
||||
int sockfd = socket(config->ai_family, config->ai_socktype, config->ai_protocol);
|
||||
int sockfd = socket(config -> ai_family, config -> ai_socktype, config -> ai_protocol);
|
||||
|
||||
if(sockfd < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Socket creation failed.\n");
|
||||
@ -210,7 +211,7 @@ static void process_response(char reply[sizeof(int32_t)], char *ofile, char *dir
|
||||
char *http_version = "HTTP/1.1";
|
||||
if(strncmp(reply, http_version, strlen(http_version)) == 0) { //< Check if the remote gives a correct response header
|
||||
char cresponse_code[3]; //< Array for the response code. Response codes are always 3 digits long
|
||||
strncpy(cresponse_code ,strstr(reply, "HTTP/1.1 ") + sizeof(http_version) + 1, 3); //< Get the response code as string.
|
||||
strncpy(cresponse_code, strstr(reply, "HTTP/1.1 ") + sizeof(http_version) + 1, 3); //< Get the response code as string.
|
||||
|
||||
int response_code = strtol(cresponse_code, NULL, 10); //< Get the response code as integer
|
||||
|
||||
@ -219,56 +220,51 @@ static void process_response(char reply[sizeof(int32_t)], char *ofile, char *dir
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (response_code != 200) {
|
||||
printf("%s\n", strtok(reply, "\r\n")); //< Get the first line
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
} else {
|
||||
char *body = strstr(reply, "\r\n\r\n") + 4;
|
||||
if(ofile == NULL && dir == NULL) printf("%s", body); //< Print the body of the response
|
||||
else {
|
||||
if(ofile != NULL) {
|
||||
FILE *out = NULL;
|
||||
out = fopen(ofile, "w");
|
||||
|
||||
char *body = strstr(reply, "\r\n\r\n") + 4;
|
||||
if(ofile == NULL && dir == NULL) printf("%s", body); //< Print the body of the response
|
||||
else {
|
||||
if(ofile != NULL) {
|
||||
FILE *out = NULL;
|
||||
out = fopen(ofile, "w");
|
||||
if(out == NULL) {
|
||||
(void) fprintf(stderr, "ERROR: Could not open ./%s for writing.", ofile);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(out, "%s", body);
|
||||
fclose(out);
|
||||
printf("File ./%s successfully written.\n", ofile);
|
||||
}
|
||||
} else if(dir != NULL) {
|
||||
FILE *out = NULL;
|
||||
|
||||
if(out == NULL) {
|
||||
(void) fprintf(stderr, "ERROR: Could not open ./%s for writing.", ofile);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(out, "%s", body);
|
||||
fclose(out);
|
||||
printf("File ./%s successfully written.\n", ofile);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
} else if(dir != NULL) {
|
||||
FILE *out = NULL;
|
||||
|
||||
char path[sizeof(dir) + 100];
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
snprintf(path, sizeof path, "%sresult.html", dir);
|
||||
else
|
||||
snprintf(path, sizeof path, "%s/result.html", dir);
|
||||
|
||||
out = fopen(path, "w"); //< Open <path>/result.html for writing
|
||||
|
||||
if(out == NULL) {
|
||||
char path[sizeof(dir) + 100];
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
(void) fprintf(stderr, "ERROR: Could not open %sresult.html for writing.\n", dir);
|
||||
snprintf(path, sizeof path, "%sresult.html", dir);
|
||||
else
|
||||
(void) fprintf(stderr, "ERROR: Could not open %s/result.html for writing.\n", dir);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(out, "%s", body);
|
||||
fclose(out);
|
||||
snprintf(path, sizeof path, "%s/result.html", dir);
|
||||
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
printf("File %sresult.html successfully written.\n", dir);
|
||||
else
|
||||
printf("File %s/result.html successfully written.\n", dir);
|
||||
out = fopen(path, "w"); //< Open <path>/result.html for writing
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
if(out == NULL) {
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
(void) fprintf(stderr, "ERROR: Could not open %sresult.html for writing.\n", dir);
|
||||
else
|
||||
(void) fprintf(stderr, "ERROR: Could not open %s/result.html for writing.\n", dir);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
fprintf(out, "%s", body);
|
||||
fclose(out);
|
||||
|
||||
if(dir[strlen(dir) - 1] == '/')
|
||||
printf("File %sresult.html successfully written.\n", dir);
|
||||
else
|
||||
printf("File %s/result.html successfully written.\n", dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Protocol error.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
223
http/server.c
223
http/server.c
@ -0,0 +1,223 @@
|
||||
#define _GNU_SOURCE ///< in order for optarg to be defined
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include "shared/http.h"
|
||||
|
||||
#define LISTEN_BACKLOG 5 //< Define maximum size of the input queue
|
||||
|
||||
static int create_server_socket(uint16_t port, struct sockaddr_in incoming);
|
||||
static char * get_file_contents(char *dir, char *file);
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
check_opts_number(argc);
|
||||
|
||||
char *cport = "8080";
|
||||
char *ifile = "index.html";
|
||||
char *dir = NULL;
|
||||
char response[sizeof(int32_t)]; //< To hold the server response
|
||||
int c;
|
||||
|
||||
/// Process the options
|
||||
while((c = getopt(argc, argv, "p:i:h")) != -1) {
|
||||
switch(c) {
|
||||
case 'p':
|
||||
cport = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
ifile = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
printf("Command usage:\n");
|
||||
print_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
case '?':
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the required argument is there
|
||||
if(argv[optind] == NULL) {
|
||||
fprintf(stderr, "Mandatory argument 'doc_root' missing.\n");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
dir = argv[optind];
|
||||
}
|
||||
|
||||
uint16_t port = atoi(cport); //< Convert the argument to integer
|
||||
|
||||
struct sockaddr_in incoming; //< Basic incoming config
|
||||
|
||||
incoming.sin_addr.s_addr = INADDR_ANY; //< Allow 0.0.0.0 to access the server
|
||||
incoming.sin_port = htons(port); //< Set the port to work on
|
||||
incoming.sin_family = AF_INET; //< Use IPv4
|
||||
|
||||
int sockfd = create_server_socket(port, incoming); //< Define a server socket
|
||||
|
||||
socklen_t peer_addr_size = sizeof(struct sockaddr_in);
|
||||
|
||||
/// Start accepting connections
|
||||
int current_session = accept(sockfd, (struct sockaddr*) &incoming, &peer_addr_size); //< Save the current session
|
||||
|
||||
if(current_session < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Could not start accepting connections\n");
|
||||
close(sockfd);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
char request[sizeof(int32_t)];
|
||||
|
||||
if(recv(current_session, request, strlen(request) - 1, 0) < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Could not receive connection from client\n");
|
||||
close(current_session);
|
||||
close(sockfd);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
char method[4];
|
||||
strncpy(method, request, 4); //< Get the request type
|
||||
method[3] = 0; //< Add terminating char
|
||||
|
||||
if(strcmp(method, "GET") == 0) {
|
||||
(void) fprintf(stdout, "Got here\n");
|
||||
close(current_session);
|
||||
close(sockfd);
|
||||
} else {
|
||||
fprintf(stderr, "ERROR: Unhandled HTTP request method received\n");
|
||||
char header[sizeof(int8_t) + 100];
|
||||
time_t t = time(NULL);
|
||||
struct tm tm = *localtime(&t);
|
||||
snprintf(
|
||||
header,
|
||||
sizeof header,
|
||||
"HTTP/1.1 501 Not Implemented\r\nDate: %d, %d %d %d %d:%d:%d %s\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
|
||||
tm.tm_wday, tm.tm_mday, tm.tm_mon, tm.tm_year + 1900, tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_zone
|
||||
); //< Build the header
|
||||
|
||||
puts(header);
|
||||
strncpy(response, header, sizeof(header));
|
||||
}
|
||||
close(current_session);
|
||||
close(sockfd);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send the response to the client
|
||||
if(send(current_session, response, strlen(response) + 1, 0) < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Could not send the response to the client\n");
|
||||
close(current_session);
|
||||
close(sockfd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(current_session);
|
||||
close(sockfd);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void print_usage(void) {
|
||||
printf("server [-p PORT] [ -i INDEX ] DOC_ROOT\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to check the number of arguments
|
||||
* @details Checks the number of arguments supplied to the command line.
|
||||
* If the arguments are less than the required or more than possible, it exits
|
||||
* @param argc - Number of arguments obtained from the command line
|
||||
* @return none
|
||||
*
|
||||
**/
|
||||
static void check_opts_number(int argc) {
|
||||
if(argc <= 1) {
|
||||
fprintf(stderr, "At least one argument expected.\n");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
} else if(argc > 6) {
|
||||
fprintf(stderr, "Too many arguments supplied.\n");
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to create a socket using a default configuration for a server
|
||||
* @details Creates a socket using a default configuration for a server
|
||||
* If there was no error, it returns the socket
|
||||
* If there was an error, it exits and displays an error message
|
||||
* @param port - the port to bind the server to
|
||||
* incoming - basic incoming sockaddr config
|
||||
* @return sockfd - The socket obtained from the socket creation
|
||||
**/
|
||||
static int create_server_socket(uint16_t port, struct sockaddr_in incoming) {
|
||||
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //< Define socket with default configuration
|
||||
|
||||
if(sockfd < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Socket creation failed.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/// Bind to a port
|
||||
if(bind(sockfd, (struct sockaddr*) &incoming, sizeof(struct sockaddr_in)) < 0) {
|
||||
(void) fprintf(stderr, "ERROR: Could not bind server to port %d!\n", port);
|
||||
close(sockfd);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/// Start listening on the socket
|
||||
if(listen(sockfd, LISTEN_BACKLOG) < 0)
|
||||
{
|
||||
(void) fprintf(stderr, "ERROR: Could not start listening\n");
|
||||
close(sockfd);
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
(void) fprintf(stdout, "Listening on port %d...\n", port);
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function to take the contents of a specified file
|
||||
* @details Gets the contents of the file specified
|
||||
* If there is no file, it returns NULL
|
||||
* If the file is empty, it returns " "
|
||||
* If the file is not empty, it returns its contents
|
||||
* @param dir - the directory of the file
|
||||
* filename - the name of the file
|
||||
* @return res - The contents of the file, formatted as a string
|
||||
**/
|
||||
static char * get_file_contents(char *dir, char *filename) {
|
||||
char *path = strcpy(dir, filename);
|
||||
|
||||
FILE *file = NULL;
|
||||
|
||||
file = fopen(path, "r");
|
||||
|
||||
if(file == NULL) {
|
||||
return NULL;
|
||||
} else {
|
||||
char *res = 0;
|
||||
|
||||
fseek(file, 0, SEEK_END); //< Set position to end of stream
|
||||
int file_length = ftell(file); //< Get file length
|
||||
fseek(file, 0, SEEK_SET); //< Set the position to begining of stream
|
||||
res = malloc(file_length); //< Allocate memory for the file contents
|
||||
|
||||
if(res != 0) {
|
||||
fread(res, 1, file_length, file); //< Read the contents into res
|
||||
} else {
|
||||
return " ";
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
@ -59,7 +59,6 @@ int main(int argc, char *argv[]) {
|
||||
printf("Command usage:\n");
|
||||
print_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
break;
|
||||
case '?':
|
||||
print_usage();
|
||||
exit(EXIT_FAILURE);
|
||||
|
Reference in New Issue
Block a user