Internet Programming
RSS icon Email icon Home icon
  • Lab #4 CGI Web Server

    Posted on April 8th, 2009 admin 1 comment

    In this lab you will improve your simple web server so that it can use the POST method and can run CGI scripts.

    You should look at the file extension to decide the type of the file. If the extension is “.cgi” or “.pl”, then run the program directly. If the extension is “.php”, then you must launch the php binary while setting the appropriate environment variables.

    You should already be sending the following content-types:

    • Content-Type: text/html – for files with the .html suffix
    • Content-Type: text/plain – for files with the .txt suffix
    • Content-Type: image/jpg – for files with the .jpg suffix
    • Content-Type: image/gif – for files with the .gif suffix

    Resources

    • CGI Made Really Easy
    • Perl CGI scripts
      • Bates Ch. 9
      • Bates Ch. 10
    • The following HINTS will be useful to get CGI working
      • Be sure all your responses send HTTP/1.0 as the version number.
      • All http headers should be encoded as environment variables for the CGI program and passed in the env array in execve.
      • Read the request from the socket 1 character at a time to process the headers:
        • Read off the first line to get the url and request method
        • Read each additional line until you get a blank line
        • Each line corresponds to an HTTP header. (You don’t need to worry about multi-line headers)
        • All http header names should be ALL_CAPS and any dashes should be replaced with underscores.
        • The lines will be in the format Header-name: value. You must translate that to HEADER_NAME=value.
        • (Make sure you ONLY modify the header NAME, not the value)
        • AFTER you have translated the header lines as shown above, you must prepend HTTP_ to all

          headers except for the CONTENT_LENGTH and CONTENT_TYPE headers.

      • When you create the environment array for the cgi program, make sure all character array entries are of the form VAR=value with no whitespace between the VAR and the = and the first character in the value.
      • For CGI to work properly, you should add the following environment variables to those sent as HTTP headers from the client:
        • GATEWAY_INTERFACE=CGI/1.1
        • REQUEST_URI=%s where %s is the url
        • REQUEST_METHOD=%s where %s is either POST or GET
        • QUERY_STRING=%s where %s is everything after ? in the url
      • Here is a sample page from my browser showing the output of my Printenv.cgi to show you what your environment variables should look like.

    • Once you have forked the child to run the CGI script, you must use dup2 to change stdin and stdout to a pipe from the parent process.
    • Finally, the HandleConnection thread must fully read the request and send everything after the blank line to the CGI script using the pipe.
    • Sample Code – You should use this sample.cpp in writing your web server. It will save you a lot of time.
    • The question that is often asked is “How does the HandleConnection thread know how much to read from the client on the socket?” The web browser always sends a CONTENT_LENGTH header in a POST request. You should read that many bytes from the socket and write that many bytes to the pipe. Then close the pipe. If it is a GET request, the HandleConnection thread should just close the pipe. Here is some pseudo code (I do not guarantee the syntax):
              int ServeToCGIpipefd[2];
              int CGIToServepipefd[2];
      	pipe(ServeToCGIpipefd);
              pipe(CGIToServepipefd);
      
      	pid = fork();
      	if (pid == 0) // I am the child who is going to exec the CGI script
      		close(ServeToCGIpipefd[1]);    // close the write side of the pipe from the server
      		dup2(ServeToCGIpipefd[0], 0);  // dup the pipe to stdin
                      close(CGIToServepipefd[0]);    // close the read side of the pipe to the server
      		dup2(CGIToServepipefd[1], 1);  // dup the pipe to stdout
      
                      // set up args and env
      		execve(....)
      	else // This is the parent
      		close(ServeToCGIpipefd[0]);  // close the read side of the pipe to the CGI script
                      close(CGIToServepipefd[1]);  // close the write side of the pipe from the CGI script
                      GetLine(socket) // Read the request and parse the url
                      GetHeaderLines(vector, socket, TRUE) // Read in the header lines, changing the format
      
                      // Put all of the header information into environment variables
      		if request is a POST
      			get content length from the request headers
      			amtread = 0
      			while (amtread < contentlength && amt = read(socket, buffer, MAXBUFLEN) )
      				amtread += amt
      				write (ServeToCGIpipefd[1], buffer, amt);
      
                      // Read from the CGIToServePipefd[0] until you get an error and write this data to the socket
      
      		close(ServeToCGIpipefd[1]); // all done, close the pipe
                      close(CGIToServepipefd[0]); // all done, close the pipe
    • If you would like to get simple php working, you should use php-cgi (type which php-cgi on the lab machines to find out where the executable is). When you get a url with .php, you will then launch php-cgi. php will get the scriptname and location from these additional environment variables which you must pass in the env array in execve.
      • SCRIPT_FILENAME=%s where %s is the full path to the script
      • SCRIPT_NAME=%s where %s is the url (same as REQUEST_URI) of the script

      Also, you need to pass in the REDIRECT_STATUS environment variable. If you try executing php-cgi without it, you’ll probably get and error message. I think a value of 200 works.

      To get sessions working, you will need to specify a different directory to store the session data. In your php scripts, issue the command session_save_path ("./"); just prior to your session_start();


      You can use and modify this fileto debug and test your Perl CGI GET and POST methods.Pass your code off with the TA, and post the code on your class website.

      Passoff Level Behavior Points

      Minimal Passoff

      You can run simple CGI scripts

      70%

      Perfect Passoff

      You implement POST as well as GET. You can run the perl CGI registration script from the previous assignment.

      100%

      Extra Credit

      You can run simple PHP scripts using your web server and the CGI version of PHP.

      +5%

      Extra Credit

      Early Passoff.

      +5%

      Details

      • This
        simple cgi file
        can be used to test your cgi functionality. You will
        have to save it away as test5.cgi and execute it when it is accessed. It simply produces a html file that you then send to the browser.
        Before sending the file to the browser, you need to send the

        "HTTP/1.0 200 OK"
        
        "MIME-Version:1.0"

        strings. The CGI script will send the content type and everything else. You can access it with a url that looks something like the following.  If you put it in your cgi-bin directory, then you can test it with the normal web server for “students” to see what the output should be.

      http://flute.cs.byu.edu:1236/students/cs460ta/public_html/cgi-bin/test5.cgi

      • This more complicated cgi file must be passed arguments. You will have to save it away as test6.cgi and execute it when it is accessed. In order to pass off this part of the lab, you will have to set up the environment variables correctly.  You will only have to implement the GET request method.  It simply produces a html file that you then send to the browser.You can access it with a url that looks something like the following.  If you put it in your cgi-bin directory, then you can test it with the normal web server for “students” to see what the output should be.  The teststring should appear in the QUERY_STRING in the environment and the REQUEST_METHOD and SERVER_PORT should also be correct.  If this were a POST request, then the CONTENT_LENGTH would contain the length of the data

        being sent through stdin to the script.  You might want to take a look at implementing the POST method if you are ambitious. You would normally do this from a form, not by just modifying the url in your browser.

      http://flute.cs.byu.edu:1236/students/cs460ta/public_html/cgi-bin/test6.cgi?teststring

     

    1 responses to “Lab #4 CGI Web Server” RSS icon

    • Safari will resend a request three times if it doesn’t receive a response code. I spent a lot of time debugging what I thought were problems with my semaphore code when in reality I actually received three requests on the same connection.


    Leave a reply

    You must be logged in to post a comment.