CGI stands for Common Gateway Interface; it is an API for writing applications (often scripts) that can be run by a Web server to service a particular range of URLs. CGIs can perform dynamic activities like automatically generating Web documents. More important, they can accept data sent from the browser; they are most frequently used to process forms. The name=value pairs of the form fields are encoded by the browser in a special format and sent to the application using one of two methods. The first method, called GET, places the user's input directly into the URL and requests the corresponding document. The server recognizes that the first part of the URL refers to a CGI program and invokes it, passing along the information encoded in the URL as a string parameter. The second method for submitting data to a server is called POST. In this method, the browser uses the HTTP POST command to ask the server to accept the encoded data and pass it to the CGI program as a stream. Most CGI programs use utilities that shield them from the details of receiving the data and decoding it. But because of common limitations, the POST method is more suitable for sending large amounts of data.
In Java, we can create a URL that refers to a CGI program and send it data using either the GET or POST methods. Why would we want to talk to a CGI? Well, they are currently the most widely used technique for building advanced Web applications. Other techniques, like opening a socket or writing a Java servlet, are coming on strong, but CGI has been in widespread use for several years. Another important reason for using CGI is that many firewalls block socket connections entirely. But all firewalls that allow Web access have to let us use GET and POST to talk to CGIs. So CGI programs can be used as a last resort communications mechanism between applets and servers.
Using the GET method is pretty easy. All we have to do is create a URL pointing to a CGI program and use a simple convention to tack on the encoded name=value pairs that make up our data. For example, the following code snippet opens a URL to a CGI program called login.cgi on the server myhost and passes it two name=value pairs. It also prints whatever text the CGI sends back:
URL url = new URL(
"http://myhost/cgi-bin/login.cgi?Name=Pat&Password=foobar");
BufferedReader bin = new BufferedReader (
new InputStreamReader( url.openStream() ));
String line;
while ( (line = bin.readLine()) != null )
System.out.println( line );
To form the new URL, we start with the URL of login.cgi; we
add a question mark(?), which marks the beginning of the data,
followed by the first name=value pair. We can add as many pairs as we want,
separated by ampersand (&) characters. The rest of our code opens the
stream and
reads back the data. Remember that creating a URL doesn't actually
open the connection. In this case, the URL connection was made
implicitly when we called openStream().
Although we are assuming here that our CGI sends back text,
it could send anything. We could use the
getContentType()
method of the URL to check the MIME type of any returned data, and
try to retrieve the data as an object using
getContent(). Finally, it's important to point out that we have skipped a step here.
This example works because our name=value pairs happen to be simple
text. If any "non-printable" or special characters
(including "?" or "&") are in the data itself, they have to be encoded first.
The java.net.URLEncoder class provides a
utility for encoding the data. We'll show how
to use it in the next example.
Let's create an applet that acts like a simple form. It presents two text fields, Name and Password, and a Post button that sends the data to a specified URL:
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class Post extends java.applet.Applet implements ActionListener {
TextField nameField, passwordField;
GridBagConstraints constraints = new GridBagConstraints();
void addGB( Component component, int x, int y ) {
constraints.gridx = x; constraints.gridy = y;
add ( component, constraints );
}
public void init() {
Button postButton = new Button("Post");
postButton.addActionListener( this );
setLayout( new GridBagLayout() );
addGB( new Label("Name:"), 0,0 );
addGB( nameField = new TextField(20), 1,0 );
addGB( new Label("Password:"), 0,1 );
addGB( passwordField = new TextField(20),1,1 );
Panel p = new Panel();
p.add ( postButton );
constraints.gridwidth = 2;
addGB( p, 0,2 );
}
public void actionPerformed(ActionEvent e) {
postData();
}
private void postData() {
StringBuffer sb = new StringBuffer();
sb.append( URLEncoder.encode("Name") + "=" );
sb.append( URLEncoder.encode(nameField.getText()) );
sb.append( "&" + URLEncoder.encode("Password") + "=" );
sb.append( URLEncoder.encode(passwordField.getText()) );
String formData = sb.toString();
try {
URL url = new URL( getParameter("postURL") );
HttpURLConnection urlcon = (HttpURLConnection)url.openConnection();
urlcon.setRequestMethod("POST");
urlcon.setRequestProperty("Content-type",
"application/x-www-form-urlencoded");
urlcon.setDoOutput(true);
urlcon.setDoInput(true);
PrintWriter pout = new PrintWriter( new OutputStreamWriter(
urlcon.getOutputStream(), "8859_1"), true );
pout.print( formData );
pout.flush();
// read results...
if ( urlcon.getResponseCode() != HttpURLConnection.HTTP_OK ) {
System.out.println("Bad post...");
return;
}
InputStream in = urlcon.getInputStream();
// ...
} catch (MalformedURLException e) {
System.out.println("Bad postURL");
} catch (IOException e2) {
System.out.println("I/O error: "+e2);
}
}
}
To use this applet, you must specify the URL of your CGI program by
using the "postURL" HTML parameter. For example:
<param name="postURL" value="http://myhost/cgi-bin/post-login.cgi" >The beginning of the applet creates the form; there's nothing here that won't be obvious after you've read the chapters on AWT. All the magic happens in the private
postData()
method. First we create a StringBuffer
and use it to append
name=value pairs, separated by ampersands. (We don't need the initial
question mark when we're using the POST method.) Each pair is
first encoded using
the static URLEncoder.encode() method.
We ran the name fields
through the encoder as well as the value fields, even though we know from the
first example that they contain no special characters, and we didn't have to.Next we set up the connection to the CGI program. In our previous
example, we didn't
have to do anything special to send the data, because the request was made
by the Web browser for us. Here, we have to carry some of the weight
of talking to the remote Web server. Fortunately, the
HttpURLConnection
object does most of the work for us; we just have to tell it that we
want to do a POST to the URL and the type of data we are sending. We ask
for the URLConnection object using the
URL's openConnection() method.
We know that we are using HTTP, so we should be able to cast it to an
HttpURLConnection type, which has the
support we need.
Next we use setRequestMethod() to tell the
connection we want to POST.
We also use setRequestProperty() to set
the "Content-Type" field of our
HTTP request to the appropriate type--in this case, the proper MIME type
for encoded form data. (This helps the CGI sort out what we're sending.)
Finally, we use the setDoOutput() and
setDoInput() methods to tell the
connection that we want to send and receive stream data.
We get an output stream from the connection with
getOutputStream() and
create a PrintWriter so we can easily
write our encoded data.
After we post the data, we call
getResponseCode() to check the HTTP
response
code from the server to see if it indicates the POST was successful. Other
response codes (defined as constants in
HttpURLConnection) indicate
various failures. At the end, we indicate where we could read back the
response. I haven't implemented anything; you can add your own code if
you want.
Although form-encoded data is the most common, other types of communications are possible. We could have used the input and output streams to exchange arbitrary data with the CGI.
HTTPS is the standard HTTP protocol run over SSL (Secure Socket Layer) sockets, which use public-key encryption techniques to encrypt the data sent. Most browsers currently come with built-in support for HTTPS (or raw SSL sockets). Therefore, if the server supports HTTPS, you can use the browser to send and receive secure data simply by specifying the https: protocol in your URLs.