Thread: rewriting my server

  1. #1
    Registered User
    Join Date
    Mar 2006
    Posts
    150

    rewriting my server

    At the advice of people on this forum, I rewrote my server to use std::string, and here's the code i have:
    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!

  2. #2
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    What line does it crash at? (Compile with debugging information. If you use gcc, -g on the command line you're compiling with. Then run through gdb.)

    Also, a couple notes:
    socket_read():
    Code:
      temp = recv (to_read, recv_buf, 1024, 0);
    
      if (temp == SOCKET_ERROR || temp == 0)
      {
       read = temp;
       break;
      }
    
      buffer += recv_buf;
    You never null-terminate recv_buff. (The += operator expects it to be.)

    read_line(): Lookup fgets(), so you're not reading a character at a time.

    SendHTTPError():
    Code:
    buffer = new char [length]; // Line 468
    buffer is not freed. Free the buffer.

    main(): client_addr is never initialized/filled out. Did you mean to pass it to accept()?
    main(): contents is never freed. (WSACleanup() too, and you miss some fclose()s when errors occur.)
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  3. #3
    Registered User
    Join Date
    Mar 2006
    Posts
    150
    hey thanks alot. I do tend to miss stuff alot. Im using Dev-cpp, how do i debug using that? I dont usually debug, even though i probably should...

    now that i fixed all of those things, the establish() function won't work. Im gonna debug it

    turns out my old server was bound to port 80.. oops. so heres the main function again:

    Code:
    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, (struct sockaddr *) &client_addr, &addr_len);
    
      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;
    }
    for some reason, my code will not pick up a socket from accept (). i get the WSA error 10014, some crap about an invalid address space or something. I have no clue what is going on here. I never had this problem with accept() on my old version, and it's written the exact same way!
    Last edited by xixpsychoxix; 06-30-2008 at 09:48 PM. Reason: more info found

  4. #4
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    You need to initialize addr_len. Set it to sizeof(client_addr) before passing it to accept().
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  5. #5
    Registered User
    Join Date
    Mar 2006
    Posts
    150
    It's definitely still crashing somewhere, but for some reason my compiler won't let me debug. How exactly does debugging with gcc work?

  6. #6
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    In general, you compile each file as usual, but you add the "-g" switch to the command line, like:
    Code:
    gcc -g -Wall -c -o file.o file.c
    Then you link your program, and execute:
    Code:
    gdb your_program_name
    gdb starts, and you type "run". Your program runs, and crashes as usual. gdb can sometimes tell you where it crashes immediately, but sometimes you have to do thing like run a backtrace (type bt or backtrace). A backtrace just shows who calls what functions, and with what arguments, and can usually show where things go wrong, or how far they got.

    Google around the web, there's tutorials out there. There's probably some help on getting it set up in Dev-C++ too.
    (Alternatively, you could attach your code/settings file, so that someone here could look, but being able to use a debugger like gdb will save you many hours of bug hunting, I highly recommend you get to know a debugger. Like everything, it takes practice to get used to.)
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. server client application - (i really need your help)
    By sarahnetworking in forum C Programming
    Replies: 3
    Last Post: 03-01-2008, 10:54 PM
  2. Server Architecture
    By coder8137 in forum Networking/Device Communication
    Replies: 2
    Last Post: 01-29-2008, 11:21 PM
  3. Where's the EPIPE signal?
    By marc.andrysco in forum Networking/Device Communication
    Replies: 0
    Last Post: 12-23-2006, 08:04 PM
  4. IE 6 status bar
    By DavidP in forum Tech Board
    Replies: 15
    Last Post: 10-23-2002, 05:31 PM
  5. socket question
    By Unregistered in forum C Programming
    Replies: 3
    Last Post: 07-19-2002, 01:54 PM