Code:
/* server.cpp: A windows-based server designed for basic HTTP communication */
#include <windows.h>
#include <winsock2.h>
#include <iostream>
#include <stdio.h>
#include <string>
#include <process.h>
#include <cstdlib>
using namespace std;
#define PARSE_SUCCESS 1
#define PARSE_FAILURE -1
#define ACCEPT_ERROR -2
#define READ_FAILURE -4
#define WRITE_FAILURE -8
#define ERR_SEND_SUCCESS 0
#define ERR_SEND_FAIL 1
// this section defines the HTTP status codes for the server
#define OK "200" //used
//redirection status codes in case of redirect, currently used marked //used
#define PERMANENT_MOVE "301"
#define FOUND_ELSEWHERE "302"
#define SEE_OTHER_URI "303"
#define TEMPORARY_REDIRECT "307"
//client status codes for request errors, currently used marked //used
#define BAD_REQUEST "400" //used
#define UNAUTHORIZED "401"
#define FORBIDDEN "403" //used
#define NOT_FOUND "404" //used
#define METHOD_NOT_ALLOWED "405"
#define GONE "410"
//server status codes for server errors, currently used marked //used
#define INTERNAL_ERROR "500" //used
#define NOT_IMPLEMENTED "501" //used
#define SERVICE_UNAVAILABLE "503"
#define BAD_HTTP_VER "505" //used
// program structures
struct ClientData
{
in_addr client_ip;
SOCKET client_socket;
};
struct RequestData
{
string method;
string url;
string filepath;
string http_ver;
string headers;
};
struct MimeTypes
{
string file_ext;
string mime;
};
struct Settings
{
string serv_file_path;
string default_file;
string serv_err_path;
string serv_err_log;
string startup_log_path;
};
//function prototypes
SOCKET establish (unsigned short); //done
int get_mime_type (string); //done
int socket_read (SOCKET, string&); //done
int ParseHTTPRequest (string, RequestData&); //done
void handle_client (void *); //done
int SendHTTPError (char *, const char *, SOCKET); //done
int read_line (FILE *, string&); //done
int write_line (FILE *, string); //done
void lower (string&); //done
int store_settings (char *); //done
//global vars
Settings serv_set;
MimeTypes content_types[] = {{ ".txt", "text/plain" },
{ ".html", "text/html" },
{ ".htm" , "text/html" },
{ ".gif" , "image/gif" },
{ ".jpg" , "image/jpeg" }};
//main function
int main ()
{
int addr_len, settings_len, read;
WSADATA wsa_dat;
SOCKET serv_sock;
struct sockaddr_in client_addr;
ClientData *client_dat;
FILE *infile, *file_out;
string serv_error, success;
char *contents;
infile = fopen ("c:\\settings.dat", "rb");
if (!infile)
{
cout << "infile error!!" << endl;
system ("pause");
return 0;
}
fseek (infile,0,SEEK_END);
settings_len = ftell (infile);
fseek (infile,0,SEEK_SET);
contents = new char [settings_len];
read = fread (contents, sizeof (char), settings_len, infile);
if (read != settings_len)
{
return 0;
}
store_settings (contents);
fclose (infile);
file_out = fopen (serv_set.startup_log_path.c_str(), "ab");
if (file_out == NULL)
{
cout << "Error opening startup log!" << endl;
system ("pause");
return 0;
}
if (WSAStartup (MAKEWORD (1,1), &wsa_dat) == SOCKET_ERROR)
{
cout << "Error with WSAStartup!" << endl;
serv_error = "Error with WSAStartup!!!\n\n";
write_line (file_out, serv_error);
fclose(file_out);
return 0;
}
success = "Web Server Started!!!\n\nSettings:\nServer Path: ";
success += serv_set.serv_file_path;
success += "\r\nDefault Resource: ";
success += serv_set.default_file;
success += "\r\nServer Error Files Path: ";
success += serv_set.serv_err_path;
success += "\r\n";
cout << success.c_str();
fwrite (success.c_str(), sizeof (char), success.length(), file_out);
serv_sock = establish (80);
if (serv_sock == INVALID_SOCKET)
{
cout << "Error establishing server socket!!!" << endl;
serv_error = "Error establishing server socket!!!\n\n";
write_line (file_out, serv_error);
fclose(file_out);
system ("pause");
return 0;
}
system ("pause");
for (;;)
{
SOCKET new_socket = accept (serv_sock, NULL, NULL);
if (new_socket == INVALID_SOCKET)
{
cout << "Error accepting connection!" << endl;
cout << WSAGetLastError () << endl;
serv_error = "Error accepting connection!\n";
write_line (file_out, serv_error);
fclose (file_out);
system ("pause");
return 0;
}
client_dat = new ClientData;
client_dat->client_socket = new_socket;
memcpy (&(client_dat->client_ip), &client_addr.sin_addr.s_addr, 4);
cout << "Got connection from: " << inet_ntoa (client_addr.sin_addr) << endl << endl;
system ("pause");
_beginthread(handle_client, 0, (void *) client_dat);
}
return 0;
}
//function handle_client is a thread designed to handle all of the actions of the server
void handle_client (void *client_dat)
{
SOCKET client_socket;
int num_chars;
string http_req, response, error;
RequestData request_dat;
FILE *outfile;
outfile = fopen (serv_set.serv_err_log.c_str(), "wb+");
if (!outfile)
{
cout << "Server error!" << endl;
system ("pause");
return;
}
client_socket = ((ClientData *) client_dat)->client_socket;
delete client_dat;
num_chars = socket_read (client_socket, http_req);
if (num_chars == SOCKET_ERROR || num_chars == 0)
{
error = "socket_read() error\n";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (INTERNAL_ERROR, "Internal Server Error", client_socket);
closesocket (client_socket);
return;
}
cout << "HTTP Request Data" << endl << http_req.c_str() << endl << endl;
system ("pause");
if (http_req.empty())
{
error = "socket_read() error\n";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (INTERNAL_ERROR, "Internal Server Error", client_socket);
closesocket (client_socket);
system ("pause");
return;
}
if (http_req.find ("Host:") == string::npos)
{
error = "No Hostname was received!\n";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (INTERNAL_ERROR, "Internal Server Error", client_socket);
closesocket (client_socket);
system ("pause");
return;
}
if (ParseHTTPRequest (http_req, request_dat) == PARSE_FAILURE)
{
error = "Bad HTTP Request format!";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (BAD_REQUEST, "Bad Request", client_socket);
closesocket (client_socket);
return;
}
if (request_dat.filepath == serv_set.serv_file_path)
request_dat.filepath += serv_set.default_file;
if (request_dat.method.find ("GET") != string::npos)
{
if (request_dat.filepath.compare (0, serv_set.serv_file_path.length(), serv_set.serv_file_path))
{
FILE *resource;
long file_len, read;
char *file_dat;
cout << "File requested: " << request_dat.filepath.c_str();
resource = fopen (request_dat.filepath.c_str(), "rb");
if (!resource)
{
error = "Can't find file: ";
error += request_dat.filepath;
error += "\n";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (NOT_FOUND, "Not Found", client_socket);
closesocket (client_socket);
return;
}
fseek (resource,0,ios::end);
file_len = ftell(resource);
fseek (resource,0,ios::beg);
file_dat = new char [file_len];
num_chars = fread (file_dat, sizeof (char), file_len, resource);
if (num_chars != file_len)
{
error = "File read error!\n";
write_line (outfile, error);
fclose (resource);
fclose (outfile);
SendHTTPError (INTERNAL_ERROR, "Internal Server Error", client_socket);
closesocket (client_socket);
return;
}
fclose (resource);
response = "HTTP/1.1 200 OK\r\n";
response += "Content-Type: ";
response += content_types[get_mime_type(request_dat.filepath)].mime;
response += "\r\nContent-Length: ";
response += file_len;
response += "\r\n\r\n";
cout << "Server response: " << endl << response.c_str() << endl << endl;
send (client_socket, response.c_str(), response.length(), 0);
send (client_socket, file_dat, file_len, 0);
delete [] file_dat;
closesocket (client_socket);
}
else
{
error = "Security Violation!\n";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (FORBIDDEN, "Forbidden", client_socket);
closesocket (client_socket);
return;
}
}
else
{
error = "Unsupported Method!\n";
write_line (outfile, error);
fclose (outfile);
SendHTTPError (NOT_IMPLEMENTED, "Not Implemented", client_socket);
closesocket (client_socket);
return;
}
}
//function socket_read reads the contents of a socket into a std::string
int socket_read (SOCKET to_read, string& buffer)
{
char recv_buf[1024];
int read = 0, temp = 0;
do
{
temp = recv (to_read, recv_buf, 1024, 0);
if (temp == SOCKET_ERROR || temp == 0)
{
read = temp;
break;
}
buffer += recv_buf;
read += temp;
if (buffer.find ("\r\n\r\n") != string::npos)
break;
}
while (temp != SOCKET_ERROR && temp != 0);
return read;
}
//function read_line reads a line of data from an ifstream
int read_line (FILE *infile, string& buffer)
{
char char_read;
int bytes_read = 0;
while ((char_read = getc(infile)) != '\n' && char_read != EOF)
{
buffer += char_read;
++bytes_read;
}
if (ferror (infile))
return READ_FAILURE;
else
return bytes_read;
}
//function write_line writes a line of data to a file opened for append
int write_line (FILE *outfile, string to_put)
{
int written;
written = fwrite (to_put.c_str(), sizeof (char), to_put.length(), outfile);
if (written != to_put.length())
return WRITE_FAILURE;
return 0;
}
//function lower makes a std::string lowercase
void lower (string& to_lower)
{
int index = 0;
while (to_lower[index] != '\0')
{
if (isupper (to_lower[index]))
to_lower[index] = tolower (to_lower[index]);
++index;
}
}
//function SendHTTPError sends an http error file based on a char *
int SendHTTPError (char *err_val, const char *err_string, SOCKET to_send)
{
FILE *err_file;
int length, read;
string to_open, response;
char *buffer;
to_open += serv_set.serv_err_path;
to_open += err_val;
to_open += ".html";
response += "HTTP/1.1 ";
response += err_val;
response += err_string;
response += "\r\n\r\n";
cout << response.c_str() << endl;
cout << to_open.c_str() << endl << endl;
err_file = fopen (to_open.c_str(), "rb");
if (!err_file)
{
return ERR_SEND_FAIL;
}
fseek (err_file,0,SEEK_END);
length = ftell (err_file);
fseek (err_file,0,SEEK_SET);
buffer = new char [length];
read = fread (buffer, sizeof (char), length, err_file);
if (read != length)
{
cout << "Error file could not be read!" << endl;
fclose (err_file);
return ERR_SEND_FAIL;
}
fclose(err_file);
send (to_send, response.c_str(), response.size(), 0);
send (to_send, buffer, length, 0);
return ERR_SEND_SUCCESS;
}
//function establish sets up a socket to listen for connections
SOCKET establish (unsigned short portnum)
{
char computer_name[256];
SOCKET new_sock;
struct sockaddr_in sa;
struct hostent *hp;
sa.sin_family = AF_INET;
sa.sin_port = htons (portnum);
sa.sin_addr.s_addr = htonl (INADDR_ANY);
new_sock = socket (AF_INET, SOCK_STREAM, 0);
if (new_sock == INVALID_SOCKET)
return INVALID_SOCKET;
if (bind (new_sock, (struct sockaddr *) &sa, sizeof (struct sockaddr_in)) == SOCKET_ERROR)
{
closesocket (new_sock);
return INVALID_SOCKET;
}
if (listen (new_sock, 20) == SOCKET_ERROR)
{
closesocket (new_sock);
return INVALID_SOCKET;
}
return new_sock;
}
//function ParseHTTPRequest splits the http request into its parts
int ParseHTTPRequest (string request, RequestData& request_data)
{
char *pos, *file_path, text[request.length()+1], *fullpath_text;
strncpy (text, request.c_str(), request.length());
pos = strtok (text, " ");
if (pos == NULL)
return PARSE_FAILURE;
request_data.method = pos;
pos = strtok (NULL, " ");
if (pos == NULL)
return PARSE_FAILURE;
request_data.url = pos;
pos = strtok (NULL, "\r\n");
if (pos == NULL)
return PARSE_FAILURE;
request_data.http_ver = pos;
pos = strtok (NULL, "\0");
if (pos == NULL)
return PARSE_FAILURE;
request_data.headers = pos;
request_data.filepath = serv_set.serv_file_path;
request_data.filepath += request_data.url;
fullpath_text = new char [request_data.filepath.length()];
strncpy (fullpath_text, request_data.filepath.c_str(), request_data.filepath.length());
_fullpath (fullpath_text, file_path, MAX_PATH);
request_data.filepath = file_path;
return PARSE_SUCCESS;
}
//function get_mime_type returns the mime association for the requested resource
int get_mime_type (string requested_file)
{
string extension;
int pos;
pos = requested_file.find_last_of ('.');
extension = requested_file.substr (pos, (requested_file.length() - pos));
int num_elements = sizeof (content_types) / sizeof (MimeTypes);
for (int count = 0; count < num_elements; ++count)
{
if (content_types[count].file_ext == extension)
return count;
}
return 0;
}
//function store_settings stores the settings from a c-string into a Settings struct
int store_settings (char *settings)
{
char *pos;
pos = strtok (settings, "\n");
if (pos == NULL)
return -1;
pos[strlen(pos)-1] = '\0';
serv_set.serv_file_path = pos;
pos = strtok (NULL, "\n");
if (pos == NULL)
return -1;
pos[strlen(pos)-1] = '\0';
serv_set.default_file = pos;
pos = strtok (NULL, "\n");
if (pos == NULL)
return -1;
pos[strlen(pos)-1] = '\0';
serv_set.serv_err_path = pos;
pos = strtok (NULL, "\n");
if (pos == NULL)
return -1;
pos[strlen(pos)-1] = '\0';
serv_set.serv_err_log = pos;
pos = strtok (NULL, "\n");
if (pos == NULL)
return -1;
pos[strlen(pos)-1] = '\0';
serv_set.startup_log_path = pos;
return 0;
}
for some reason after the server receives a connection, it crashes. Can anyone see my error, or multiple errors? If any errors are spotted here, please, report them. This is baffling me!