-
Lab #4 CGI Web Server
Posted on April 8th, 2009 1 commentIn 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 yoursession_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



Jake Cahoon October 29th, 2009 at 22:09