Thread: reading a file to send to an http client

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

    reading a file to send to an http client

    I recently got into developing my own web server and am not doing too bad, but i have a problem. When i try to load a web page that contains an image, the web page loads but the image does not. I downloaded, compiled, and borrowed some examples from Stuart Patterson's Charlotte web server, and the code for reading the file i pretty much used line for line. The thing is, it reads most files right, but not images as far as i can tell. Here is the code for that:
    Code:
    if (strstr (request_dat.method, "GET"))
     {
      FILE *infile;
      long len;
      char *file_dat;
    
      infile = fopen (request_dat.filepath, "rb");
     
      if (!infile)
      {
       cout << " File Error! ";
       cout << SendHTTPError (NOT_FOUND, client_socket);
       closesocket (client_socket);
       return;
      }
     
      fseek(infile,0,SEEK_END);
      len = ftell(infile);
      fseek(infile,0,SEEK_SET);
    
      file_dat = new char [len];
      fread (file_dat,sizeof (int),len,infile);
      fclose (infile);
    
      strcpy (response, "HTTP/1.0 200 OK\r\n");
      strncat (response, "Content-Type: ", MAX_BUFFER);
      strncat (response, content_types[getMimeType(request_dat.filepath)].mime, MAX_BUFFER);
      sprintf (response+strlen(response), "\r\nContent-Length: %ld\r\n", len);
      strncat (response, "\r\n", MAX_BUFFER);
    
      cout << response << endl;
    
      send (client_socket, response, strlen (response), 0);
      send (client_socket, file_dat, len, 0);
    
      delete [] file_dat;
      closesocket (client_socket);
     }
    Here is the entire source code:

    Code:
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     * server.cpp: A simple web server made to run on the windows platform. This server  *
     * was designed for Hilltop Baptist Church as a means to run their web page. The web *
     * server was designed as a project for me, and is free to anyone who would use or   *
     * modify it. Just make sure you credit me in your work if you use ideas or any part *
     * of this web server in your code. By the way, I took alot of examples from         *
     * Stuart Patterson's web server Charlotte. And I mean, ALOT of examples!!!          *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    
    //first thing's first: include the header files
    
    #include <windows.h>
    #include <iostream.h>
    #include <fstream.h>
    #include <stdio.h>
    #include <process.h>
    
    //define some constants
    
    #define MAX_BUFFER             1024
    #define SMALL_BUF              10
    
    #define PARSE_SUCCESS          1
    
    #define BAD_REQUEST_METHOD     -1
    #define PARSE_ERROR            -2
    #define ACCEPT_ERROR           -4
    
    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
     * the definitions of all the HTTP status codes that are or will be in future  *
     * versions supported by this software. These are the codes as defined by the  *
     * HTTP 1.1 standard                                                           *
     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
    
    //supported successful status codes, currently used marked //used
    
    #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"
    #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
    
    /* * * * * * * * * * * * * * * * * * * * * *
     * create some structures for program data *
     * * * * * * * * * * * * * * * * * * * * * */
    
    //a structure to hold data on the connected client
    
    struct ClientData
    {
     in_addr client_ip;
     SOCKET  client_socket;
    };
    
    //a structure to hold data on the client's request
    
    struct RequestData
    {
     char method[SMALL_BUF];
     char url[MAX_PATH];
     char filepath[MAX_PATH];
     char http_ver[SMALL_BUF];
    };
    
    //a structure to hold the Content-Types and their equivalent file extensions
    
    struct MimeTypes
    {
     char *file_ext;
     char *mime;
    };
    
    //a structure used to hold the settings for the server, will be constant values for now
    
    struct Settings
    {
     char *serv_file_path;
     char *default_file;
     char *serv_err_path;
    };
    
    //a structure used to make a list of the http headers in the request
    
    struct header_node
    {
     char        *header_name;
     char        *header_value;
     header_node *next;
    };
    
    //global variables
    
    char     *methods[]       = { "GET", "HEAD" };
    char     *http_ver        = "1.0 ";
    Settings  serv_settings   = { "C:\\server", "\\index.html", "C:\\server\\errors\\" };
    MimeTypes content_types[] =  {{ ".txt" , "text/plain" },
                                  { ".html", "text/html"  }, 
                                  { ".htm" , "text/html"  },
                                  { ".gif" , "image/gif"  },
                                  { ".jpg" , "image/jpeg" }};
    
    //function prototypes. Those commented out not available as of yet
    
    SOCKET         establish         (unsigned short); //done
    int            getMimeType       (char *);         //done
    int            socket_read       (SOCKET, char *, int); //done
    int            ParseHTTPRequest  (char *, RequestData&); //done
    void           handle_client     (void *); //done
    int            SendHTTPError     (char *, SOCKET); //done
    char           *string_cat       (char *, char *); //done
    
    //main function
    int main ()
    {
     int client_addr_len;
     WSADATA wsaDat;
     SOCKET servSocket;
     struct sockaddr_in client_address;
     ClientData *client_dat;
    
     if (WSAStartup (MAKELONG (1,1), &wsaDat) == SOCKET_ERROR)
     {
      cout << "Problem with WSAStartup";
      exit (1);
     }
     servSocket = establish (80);
     if (servSocket == INVALID_SOCKET)
     {
      perror ("establish");
      system ("pause");
      exit (1);
     }
    
     for (;;)
     {
      SOCKET new_socket = accept (servSocket, (struct sockaddr *) &client_address, &client_addr_len);
      
      if (new_socket == INVALID_SOCKET)
      {
       cout << "Error waiting for new connection!!!";
       system ("pause");
       exit (1);
      }
      cout << "Got connection\n";
    
      client_dat = new ClientData;
      client_dat->client_socket = new_socket;
      memcpy (&(client_dat->client_ip), &client_address.sin_addr.s_addr, 4);
    
      cout << inet_ntoa (client_address.sin_addr);
    
      _beginthread(handle_client, 0, (void *) client_dat);
     }
     return 0;
    }
    
    void handle_client (void *client_dat)
    {
     SOCKET client_socket;
     int num_chars;
     char http_req[MAX_BUFFER], response[MAX_BUFFER];
     RequestData request_dat;
    
     client_socket = ((ClientData *) client_dat)->client_socket;
     free (client_dat);
    
     num_chars = socket_read (client_socket, http_req, MAX_BUFFER);
     cout << http_req;
    
     if (num_chars == SOCKET_ERROR || num_chars == 0)
     {
      cout << "SOCKET READ ERROR " << num_chars;
      SendHTTPError (INTERNAL_ERROR, client_socket);
      closesocket (client_socket);
      return;
     }
     http_req[num_chars] = NULL;
    
     if (http_req == NULL)
     {
      SendHTTPError (INTERNAL_ERROR, client_socket);
      closesocket (client_socket);
      return;
     }
     
     if (ParseHTTPRequest (http_req, request_dat) == PARSE_ERROR)
     {
      cout << "Error";
      SendHTTPError (BAD_REQUEST, client_socket);
      closesocket (client_socket);
      return;
     }
     if (strstr (request_dat.method, "GET"))
     {
      FILE *infile;
      long len;
      char *file_dat;
    
      infile = fopen (request_dat.filepath, "rb");
     
      if (!infile)
      {
       cout << " File Error! ";
       cout << SendHTTPError (NOT_FOUND, client_socket);
       closesocket (client_socket);
       return;
      }
     
      fseek(infile,0,SEEK_END);
      len = ftell(infile);
      fseek(infile,0,SEEK_SET);
    
      file_dat = new char [len];
      fread (file_dat,sizeof (int),len,infile);
      fclose (infile);
    
      strcpy (response, "HTTP/1.0 200 OK\r\n");
      strncat (response, "Content-Type: ", MAX_BUFFER);
      strncat (response, content_types[getMimeType(request_dat.filepath)].mime, MAX_BUFFER);
      sprintf (response+strlen(response), "\r\nContent-Length: %ld\r\n", len);
      strncat (response, "\r\n", MAX_BUFFER);
    
      cout << response << endl;
    
      send (client_socket, response, strlen (response), 0);
      send (client_socket, file_dat, len, 0);
    
      delete [] file_dat;
      closesocket (client_socket);
     }
     else
     {
      SendHTTPError (NOT_IMPLEMENTED, client_socket);
     }
    }
    SOCKET establish (unsigned short portnum)
    {
     char computer_name[256];
     SOCKET new_sock;
     struct sockaddr_in sa;
     struct hostent     *hp;
    
     memset      (&sa, 0, sizeof (struct sockaddr_in));
     gethostname (computer_name, sizeof (computer_name));
    
     hp = gethostbyname (computer_name);
     if (hp == NULL)
      return INVALID_SOCKET;
    
     sa.sin_family = hp->h_addrtype;
     sa.sin_port   = htons (portnum);
     
     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;
     }
     cout << "Still ok at return";
     return new_sock;
    }
    
    int getMimeType (char *requested_file)
    {
     char *pos;
     int num_elements;
    
     pos = strrchr (requested_file, '.');
     
     if (pos)
     {
      num_elements = sizeof (content_types) / sizeof (MimeTypes);
    
      for (int count = 0; count < num_elements; ++count)
      {
       if (stricmp (content_types[count].file_ext,pos) == 0)
        return count;
      }
     }
     return 0;
    }
    
    int socket_read(SOCKET client_socket, char *receivebuffer, int buffersize)
    {
    	int size = 0, totalsize = 0;
    
    	do
    	{
    		size = recv(client_socket,receivebuffer+totalsize,buffersize-totalsize,0);
    		if ( size != 0 && size != SOCKET_ERROR )
    		{
    			totalsize += size;
    
    			// are we done reading the http header?
    			if ( strstr(receivebuffer,"\r\n\r\n") )
    				break;
    		}
    		else
    			totalsize = size;			// remember error state for return
    		
    	} while ( size != 0 && size != SOCKET_ERROR );
    
    	return(totalsize);
    }
    
    int ParseHTTPRequest (char *request, RequestData& request_data)
    {
     char *pos;
    
     pos = (strtok (request, " "));
    
     if (pos == NULL)
      return PARSE_ERROR;
     
     strncpy (request_data.method, pos, SMALL_BUF);
    
     pos = strtok (NULL, " ");
      
     if (pos == NULL)
      return PARSE_ERROR;
    
     strncpy (request_data.url, pos, SMALL_BUF);
     
     pos = strtok (NULL, "\r");
     
     if (pos == NULL)
      return PARSE_ERROR;
     
     strncpy (request_data.http_ver, pos, SMALL_BUF);
    
     strcpy (request_data.filepath, serv_settings.serv_file_path);
     
     if (strcmp(request_data.url, "\\"))
       strcat (request_data.filepath, serv_settings.default_file);
     else
       strcat (request_data.filepath, request_data.url);
    
     _fullpath (request_data.filepath, request_data.filepath, MAX_PATH);
    
     return PARSE_SUCCESS;
    }
    
    int SendHTTPError (char *error, SOCKET to_send)
    {
     int length;
     FILE *infile;
     char std_err_file[MAX_PATH] = "", *contents, response[MAX_BUFFER];
    
     strcat (std_err_file, serv_settings.serv_err_path);
     strcat (std_err_file, error);
     strcat (std_err_file, ".html");
     
     strcpy (response, "HTTP/");
     strcat (response, http_ver);
     strcat (response, error);
     strcat (response, "\r\n\r\n");
    
     cout << response << endl;
     cout << std_err_file << endl;
    
     infile = fopen (std_err_file, "rb");
     
     if (!infile)
     {
      return -1;
     }
    
     fseek (infile,0,SEEK_END);
     length = ftell (infile);
     fseek (infile,0, SEEK_SET);
    
     contents = new char [length];
    
     fread (contents, sizeof (char), length, infile);
     fclose (infile);
    
     send (to_send, response, strlen (response), 0);
     send (to_send, contents, strlen (contents), 0);
     return 0;
    }
    
    char *string_cat (char *string, char *to_add)
    {
     char *return_str;
    
     return_str = (char *) malloc (strlen (string)+strlen(to_add)+1);
    
     strcat (return_str, string);
     strcat (return_str, to_add);
    
     return return_str;
    }
    can anyone please please explain to me what i may be doing wrong for reading files? I've used this code in other programs and never had a problem before!

  2. #2
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    Just glancing over it...
    • Use a sniffing program like Wireshark to ensure that what gets sent == what you think is being sent
    • Check the return from send()
    • Your call to fread() is completely wrong, read the man page. I think you're being saved from a segfault by virtue of hitting EOF right before it every time. If your file size measurement is ever wrong, you will be in trouble.
    • You assume recv()'d data to be nul-terminated in socket_read(), and it might not be so, leading to crash.

    Edit:
    • Unless your error file is nul-terminated (really odd), SendHTTPError is going to get in trouble with strlen(contents). You already have the length stored in "length", use that. Better, check how much fread() actually read.
    • SendHTTPError leaks significant amounts of memory, and opens your server to attacks through such.

    More edit:
    Haven't verified this, but what happens if I ask the following of your server?
    Code:
    GET /../../../../some_private_file_outside_of_doc_root HTTP/1.0
    Are you sure the user is only able to get files that they should be able to get?
    Even more edit:
    If you're coding in C++, why bother with all that strcpy/strcat hell? Why not use std::string?
    Last edited by Cactus_Hugger; 06-21-2008 at 11:45 PM.
    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
    FOSS Enthusiast
    Join Date
    Jun 2008
    Posts
    64
    Code:
    SOCKET establish (unsigned short portnum)
    {
     char computer_name[256];
     SOCKET new_sock;
     struct sockaddr_in sa;
     struct hostent     *hp;
    
     memset      (&sa, 0, sizeof (struct sockaddr_in));
     gethostname (computer_name, sizeof (computer_name));
    
     hp = gethostbyname (computer_name);
     if (hp == NULL)
      return INVALID_SOCKET;
    
     sa.sin_family = hp->h_addrtype;
     sa.sin_port   = htons (portnum);
     
     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;
     }
     cout << "Still ok at return";
     return new_sock;
    }
    does creating the socket even work?

    About the red-marked parts:
    What's that? Are you trying to get the servers hostname and resolve it? If that even works, you are not passing the resolved address anywhere, so you can clear that.
    But I doubt, this even works

    About the blue-marked parts:
    Look closer, its obvious why this can not work.
    1. pass AF_INET to sin_family. If youre coding the server for the internet or other IP-networks, this is always right.

    2. I can't see any assignment to sin_addr here ... despite the fact that you were trying to resolve the address above, I recommend you to use this:
    Code:
    sa.sin_addr.s_addr = INADDR_ANY;
    This is needed to tell the machine, which interfaces to use. INADDR_ANY would be 0.0.0.0, but you could also assign the ip address of a NIC or even the loopback interface (127.0.0.1) to restrict the server to listen on that NICs (or the loopback adapter)

    3. This is actually not a big deal, but socket() is supposed to get passed PF_INET, not AF_INET. This still works, because they are identical. Though they were both supposed to have different purposes. So I think it's more correct to use PF_INET, but that's your choice
    (This doesn't apply to the struct sockaddr_in!)

    I hope this helps solving
    Last edited by mkruk; 06-22-2008 at 03:08 AM.

  4. #4
    Registered User
    Join Date
    Mar 2006
    Posts
    150
    sorry guys, i dont know much about memory leaks... what makes SendHTTPError leak memory? Also, why am i getting the wrong length from this code:

    Code:
    fseek(infile,0,SEEK_END);
    len = ftell(infile);
    fseek(infile,0,SEEK_SET);
    every other program i have written to get file length has returned 15380, but this one says its 102???
    really im just starting this server. i was planning to test it alot and work out the bugs before i use it, so security isnt a big concern of mine until i get the thing at least working... sort of

  5. #5
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    It will leak memory because you're allocating memory for the local variable contents and then not deleting it before leaving the method.

    You think there's nearly 15K in an error file, which is what infile is at that point? Seems unlikely.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Newbie homework help
    By fossage in forum C Programming
    Replies: 3
    Last Post: 04-30-2009, 04:27 PM
  2. sequential file program
    By needhelpbad in forum C Programming
    Replies: 80
    Last Post: 06-08-2008, 01:04 PM
  3. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  4. System
    By drdroid in forum C++ Programming
    Replies: 3
    Last Post: 06-28-2002, 10:12 PM
  5. what does this mean to you?
    By pkananen in forum C++ Programming
    Replies: 8
    Last Post: 02-04-2002, 03:58 PM