<p>
<form action="http://www.sce.carleton.ca/netmanage/mctoolkit/cgi-bin/report.cgi"
method="POST">
Email: <input type="text" size="35" maxlength="60" name="email"><br>
Name: <input type="text" size="15" maxlength="80" name="first_name">
Surname: <input type="text" size="20" maxlength="80" name="last_name"><br>
Country: <select name="country" size="1">
<option>Select a Country </option>
<option>Australia </option>
<option>Canada </option>
<option>France </option>
<option>Germany </option>
<option>India </option>
<option>United States </option>
<option>Poland </option>
</select><br>
Type:<br>
Bug <input type="radio" name="bugType" value="bug" checked>
Request <input type="radio" name="bugType" value="request"><br>
Affected OS:
Windows 95 <input type="checkbox" name="affectedOS" value="95">
Windows NT <input type="checkbox" name="affectedOS" value="NT">
Windows CE <input type="checkbox" name="affectedOS" value="CE"><br>
Describe problem: <textarea name="purpose" rows="10" cols="45"></textarea><br>
Press to clear: <input type="reset" value="Clear">
Press when done: <input type="submit" name="submit" value="Submit"><br>
<\form>
The following GUI is generated from this HTML source:
When a client accesses the Web server asking for the Web page incorporating the form, the browser displays the form (and the rest of the HTML formatting). After filling all of the fields, the user presses the Submit button. The Clear button can be used to clear the data entry fields. The browser collects the data and includes them in a request sent to the Web browser. If the POST method is used, then the data are a part of the message body in the following form:
name=value&name=value&...
If the GET command is used (it used to be a default), then the data are appended to the URL after "?". This may cause a problem, because the Web server uses environment variables to pass this information further. Unfortinately, there are hard limits on the length of environmental variables, so some data might be lost of GET was used. Upon reception of a cgi-bin request, the Web server sets up a number of environmental variables including server_name, request_method, path_info, script_name, content_type and content_length. Next, the server executes the CGI program specified in the URL that came with the request. The CGI program, can be any application that can read from the standard input and that can write to the standard output stream. The program reads the data passed to it by the Web server through the environment. This information determines the actions undertaken by the application. The body of the message is sent to the CGI program by the server through the stanard input pipe. Using the value of content_length, which indicates the length of the body, the CGI program can parse the input and obtain the values of the data entered by the user. These data is used to generate a document, which will be passed back to the Web server, and on to the Web client. Again, the standard output pipe is used to send the document to the Web server. The CGI server may provide just the response entity, with the Web server supplying all of the remaing part, or the CGI program can format a complete message. Such solution must be indicated to the Web browser. It is done by naming CGI programs with the "nph-" prefix. This stands for non-parsed headers. The Web server passes the document to the client either as received (if coming from a CGI program whose name starts with nph-) or it appends HTTP headers and then sends the result. The client's browser receives the document and processes it as if it came directly from the Web server.
HTTP is a stateless protocol, so there are no provisions for storing data between successive interchanges of messages. This is very often required. For example, a client buying merchandise on the Internet browses through the virtual mall and collects items to buy in a basket. When the selection is made, then the Web server needs to provide details about the total amount to pay. The user, in turn, has to provide data about the payment (for example, a credit card number). This scenario is not possible in "pure" HTTP. However, there are provisions for "hidden" fields in forms, which can be used to keep all of the required data in the message that passes back and forth between the client and the server. When the client submits the initial form (items in the shopping basket), the data from this form is included in the second form that goes to the user. The user does not see this information, because it is hidden. The next submission might be, for example, the shipping address. Again, the old data (items to buy) and the address are sent to the Web server. The server hides all of these into hidden fields of a form that is presented to the user to obtain the payment information. The payment data, is returned together with the address and the items to the server, which can now process the complete transaction.
In this implementation, we will not use any database. Code to handle a databse would clutter the example, which is made to be easy to follow. The consequence of this is that the value of count is stored inside the client, because HTTP is stateless. The value is sent to the server for processing, and then the increased value is returned. This design allows for measuring the delay involved in using the CGI/HTTP technique for invoking remote operations.
The client ignores all but the last line of the message returned from the server. This is because the server does not know whether it is talking to a browser or another application. In our case, we do not need the HTTP header information (something that a browser would need).
// CountCGIClientApplet.java, Java CGI Applet
import java.awt.*;
import java.io.*;
import java.net.*;
public class CountCGIClientApplet extends java.applet.Applet
{
private TextField countField, pingTimeField;
private Button runCount;
private Counter.Count counter;
public void init()
{
// Create a 2 by 2 grid of widgets.
setLayout(new GridLayout(2, 2, 10, 10));
// Add the four widgets, initialize where necessary
add(new Label("Count"));
add(countField = new TextField());
countField.setText("1000");
add(runCount = new Button("Run"));
add(pingTimeField = new TextField());
pingTimeField.setEditable(false);
}
public boolean action(Event ev, Object arg)
{
if(ev.target == runCount)
{ try
{ String sum = "0";
// get count, initialize
start time
showStatus("Incrementing.");
int count = new Integer(countField.getText()).intValue();
long startTime = System.currentTimeMillis();
// Increment count times
for (int i = 0 ; i <
count ; i++ )
{ sum = increment(sum);
}
// Calculate stop time;
show statistics
long stopTime = System.currentTimeMillis();
pingTimeField.setText("Avg
Ping = "
+
((stopTime - startTime) / (float)count) + " msecs");
showStatus("Sum = "
+ sum);
} catch(Exception e)
{ showStatus("System Exception"
+ e);
}
return true;
}
return false;
}
public String increment(String currentSum)
{ String script = "/cgi-bin/Count.bat";
Socket socket = null;
String rdata = "";
String line = "";
String lastLine = "";
try
{
socket = new Socket("www.somewhere.net",
80);
DataOutputStream ostream
= new DataOutputStream(socket.getOutputStream());
DataInputStream istream
= new DataInputStream(socket.getInputStream());
ostream.writeBytes("POST "
+ script
+ " HTTP/1.0\r\n"
+ "Content-type:
application/octet-stream\r\n"
+ "Content-length:
"
+ currentSum.length()
+ "\r\n\r\n");
ostream.writeBytes("Increment
" + currentSum);
while ((line = istream.readLine())
!= null)
{ lastLine = line;
rdata += line
+ "\n";
}
istream.close();
ostream.close();
}
catch (Exception e)
{ showStatus("Error " + e);
if (socket !=
null)
try
{ socket.close();
}
catch (IOException ex) {}
}
return lastLine;
}
}
c:\java\bin\java CountCGIServer
import java.io.*;
import java.util.*;
class CountCGIServer
{ public static void main(String[] args)
{ int count;
String line;
try
{ // create streams
DataInputStream istream = new DataInputStream(System.in);
DataOutputStream ostream = new DataOutputStream(System.out);
// execute client requests
line = istream.readLine();
StringTokenizer tokens = new StringTokenizer(line);
String myOperation = tokens.nextToken();
// perform increment operation
if(myOperation.equals("increment"))
{ String countString
= tokens.nextToken();
count =
Integer.parseInt(countString);
count++;
ostream.writeBytes(""
+ count);
}
// close streams
istream.close();
ostream.close();
}
catch (Exception e)
{ System.out.println("Error " + e);
}
}
}