CGI/HTTP Client/Server model

Common Gateway Interface (CGI) has been created to pass the requests that cannot be served by the Web server to another application. Normally, when a Web client requests a document from a Web server, then the server locates the document and returns it in the response message. However, if the resource is not a document, but a program, the server uses the CGI to call an application that can handle the request. The external program obtains information from the Web server, processes it and generates output in a form of a MIME document (for example, an HTML document). This document is returned to the Web server, which passes it on to the requesting Web client. The client handles the document as any other document, because it has no idea that the document arrived from a source different than the Web server.
A number of auxiliary applications can be used to handle various requests. For example, they can provide interface to databases or other legacy systems.

Client/Server Scenario

The most common use of CGI is with HTML forms. As a reminder, a form tag is used to construct a Web page, which can query the user for certain input. It is done through a number of input types: data entry fields, radio butons, checkbuttons and text areas. For example, the following form could be used to collect a bug report:

<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:

Email: 
Name:  Surname: 
Country: 
Type:                 Bug  Request 
Affected OS:     Windows 95  Windows NT Windows CE 
Describe problem:

Press to clear: Press when done: 

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.

The cgi programs are managed by system administrators to minimize potential security risks. Certain additional provisions can be used to increase the security and integrity of applications in the third tier. For example, protocols like Secure Socket Layer (SSL) and secure HTTP (S-HTTP), or firewalls can be used.

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.


Count with CGI/HTTP

A CGI program can be any executable. In this particular implementation, we use Java. In our case, the CGI server must be called through a batch file. We do not use forms, so the client code has to create and send a POST request to the Web server. It can be done, because applets are allowed to connect to the servers from which they have beed loaded.

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.

HTML File for the Applet

<h1>Count CGI Client Applet</h1>
<hr>
<center>
  <applet
    code=CountCGIClientApplet.class codebase=classes width=300 height=60>
  </applet>
</center>
<hr>

The Client Applet

This code implements the event model of JDK 1.0.2.

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;
   }
}
 

CGI Batch file

The sole line in the c:\cgi-bin\Count.bat file starts a JVM with the CountCGIServer:

c:\java\bin\java CountCGIServer

The server

// CountCGIServer.java, Java CGI Server Program

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);
   }
 }
}
 

Running the Application

The following several steps are involved in running the CGI/HTTP version of Count: