CGI, Servers

How can I get a server's CGI script to send information to an applet?


(Submitted by Vivek Pabby)
Click here to view submission (112 lines)

How can I send an image over the net using the Socket, InputStream and OutputStream commands?


(Submitted by Cliff Berg)
Assuming you are getting the image from an http server, the best way to get the image is to create a URL object. This does it all for you. However, I suspect the person posing this question had more in mind, so here is a way to actually implement the http request; it can be modified for other protocols as appropriate. The technique is straigtforward: Create a socket, send the request in the format the server wants, listen for the reply and store it, and close the socket. Here is the code:

// Create a socket connection with the server
Socket socket = new Socket(hostname, port);

// Determine the socket's input and output stream handles
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());

// Write CGI-formatted command to server, requesting the data
dos.writeBytes("GET " + args[1] + " HTTP/1.0");
dos.write(0x0d);
dos.write(0x0a);
dos.write(0x0d);
dos.write(0x0a);

// Read the response, directly from the socket
long MAXLENGTH = ...the largest size the file can be...;
byte b[] = new byte[MAXLENGTH];
long c = dis.read(b, 0, MAXLENGTH);
// Note: don't make MAXLENGTH larger than read() can handle;
// you may want to add buffering to this algorithm.

// Close the socket
socket.close();

// The data we wanted is now stored in b[].

// (Note: I haven't checked this, but it should work; I've written
// other similar things.)

How can an applet call a CGI script that is expecting a POST method? A GET method?


(Submitted by Georg Wedenburg)
This code is a simple modification of the example provided in the Java Tutorial form Sun. The only thing to modify is the text to submit when do you want to make a POST or a GET:

Steps to make the submission:

POST METHOD:

  1. Create the URL to the server CGI
  2. Open the connection
  3. Open type outputstream
  4. Create the output string with the format: varname1=varvalue1&varname2=varvalue2 // and so on Note that the character '&' is the separator from variables.
  5. Close the outputstream
  6. Open the input stream
  7. Read the response from the server
  8. Close the input stream.
GET METHOD:
  1. Create the URL to the server CGI with the diference: Example:
                     'www.pp.com/pp.cgi?'+the output string with the 
                                          self format form the output
                                          string like the step 4 in the 
                                          POST METHOD.
    
  2. Open the connection
  3. Open the input stream
  4. Read the response from the server
  5. Close the input stream.
Note: The GET method does not requires an outputstream.

The following example is to submit a form with the POST method, I have tried with a lot of forms and it works well.

import java.io.*;
import java.net.*;

public class TestingTheForm {

 public static void main(String[] args) {
   try {
    URL url = new URL("TheURL");
    System.err.println("URL created:" + url.toString());

    URLConnection connection = url.openConnection();
    System.err.println("URLConnection opened ...");
						
    PrintStream outStream = new
    PrintStream(connection.getOutputStream());
    System.err.println("outStream created ...");

    outStream.println("var1=value1&var2=value2&var3=value3");
    System.err.println("the data printed ...");
    outStream.close();
    System.err.println("outStream closed ...");

			
    DataInputStream inStream = new
    DataInputStream(connection.getInputStream());
    String inputLine;
    System.err.println("inStream opened ...");
			
    while ((inputLine = inStream.readLine()) != null) {
	System.out.println(inputLine);
    }
    inStream.close();
    System.err.println("inStream closed ...");
		
  } catch (MalformedURLException me) {
     System.err.println("MalformedURLException: " + me);
  } catch (IOException ioe) {
     System.err.println("IOException: " + ioe);
  }
}
	
}
(Submitted by Jean-Luc DELATRE)
You don't need to mess around with sockets and the HTTP header to issue a POST you just need to provide the "Content-type" and "Content-length" this is an excerpt from a working applet of mine which can be seen at http://www.lga.jca.fr/dossier_web/jld/accueilcafe.html (all in french, sorry...) this however does NOT solve the long outstanding showDocument() problem (see http://www.tientien.com/tjn/post/index.html) which is the last question in your "CGI, Servers"
{
	.../...
    			
	if (! usePOST)
	{
		cgiUrl = new URL(getDocumentBase(), "cgi/bounce.cgi" + query);
       	debugOut("translated url: " + cgiUrl.toExternalForm());
   		connect = cgiUrl.openConnection();
	}
	else
	{
   	 	cgiUrl = new URL(getDocumentBase(), "cgi/bounce.cgi");
       	debugOut("translated url: " + cgiUrl.toExternalForm());
   		connect = cgiUrl.openConnection();
       	connect.setDoInput(true);
       	connect.setDoOutput(true);
       	connect.setUseCaches(false);
   		connect.setRequestProperty ("Content-type",
			"application/dont-know-what");

    	String request = "nom=" +  name.getText() + " "
			   + "email=" +  email.getText() + " "
 			   + "options=" + options.getSelectedItem() + " "
			   + "\r\n";
							
	  	connect.setRequestProperty ("Content-length",
			Integer.toString(request.length()));

       	outStream = new DataOutputStream(connect.getOutputStream());
                	
       	outStream.writeBytes(request);
	}

	DataInputStream inStream = new
		DataInputStream(connect.getInputStream());

	debugOut("Content " + connect.getContentType() +
				" length "
				+ Integer.toString(connect.getContentLength()));

	while ((inputLine = inStream.readLine()) != null) {
		debugOut(inputLine);
	}
            	
	if (usePOST) outStream.close();
	inStream.close();
}
(Submitted by Dennis M. Kowallek)
The following is from a POST on "comp.lang.java" by Ronald.Tschalaer@psi.ch. I tried it and it works!

This is probably more an http question, but basically all you need to do is create a header of the form:

  POST cgi-script HTTP/1.0
  Content-type: 
  Content-length: 
  
  The data you're posting...

Here is a simple java applet which will contact my_script.cgi, post it the stuff in sdata, and read in the reply into rdata and display it.

PostExample.java:
-----------------

import java.applet.Applet;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;

public class PostExample extends Applet
{
    private final String script = "/cgi-bin/my_script.cgi";
    private final String ctype  = "application/octet-stream";
    // note: on some servers this needs to be "application/x-www-form-urlencoded"
    // - Courtesy, John Fallows
    private       String sdata  = "Hello World";
    private       String rdata  = "";
    private       String home;
    private       int    port;

    public void init()
    {
        home = getDocumentBase().getHost();
        port = getDocumentBase().getPort();
        if (port == -1)  port = 80;
    }

    public void start()
    {
        Socket           sock;
        OutputStream     outp;
        InputStream      inp;
        DataOutputStream dataout;
        DataInputStream  datain;

        rdata = "";

        //create a client socket 

        try
            sock = new Socket(home, port);
        catch (Exception e)
        {
            rdata = e+" (socket: Host: "+home+"\tPort: "+port+")";
            return;
        }

        // Obtain output stream to communicate with the server

        try
        {
            outp = sock.getOutputStream();
            inp  = sock.getInputStream();
        }
        catch (Exception e)
        {
            rdata = e+" (getstream)";
            try
                sock.close();
            catch (IOException ee) ;
            return;
        }    

        try
        {
            dataout = new DataOutputStream(outp);
            datain  = new DataInputStream(inp);
        }
        catch (Exception e)
        {
            rdata = e+" (Dstream)";
            try
                sock.close();
            catch (IOException ee) ;
            return;
        }

        // Send http request to server and get return data

        try
        {
            // HTTP header
            dataout.writeBytes("POST " + script + " HTTP/1.0\r\n");
            dataout.writeBytes("Content-type: " + ctype + "\r\n");
            dataout.writeBytes("Content-length: " + sdata.length() + "\r\n");
            dataout.writeBytes("\r\n");         // end of header
            // POST data
            dataout.writeBytes(sdata);
			dataout.writeBytes("\r\n");
            boolean body = false;
            String line;
            while ((line = datain.readLine()) != null)
            {
                if (body)
                    rdata += "\n" + line;
                else if (line.equals(""))       // end of header
                    body = true;
            }
        }
        catch (Exception e)
        {
            rdata = e+" (write)";
            try
                sock.close();
            catch (IOException ee) ;
            return;
        }

        // close up shop

        try
        {
            dataout.close();
            datain.close();
        }
        catch (IOException e) ;
        try
            sock.close();
        catch (IOException e) ;
    }

    public void stop() {}

    public void paint(Graphics g)
    {
        StringTokenizer st = new StringTokenizer(rdata, "\n");
        int line    = 1,
            line_sp = getFontMetrics(getFont()).getHeight()+1;
 
        while (st.hasMoreTokens())
        {
            g.drawString(st.nextToken(), 5, line*line_sp);
            line++;
        }
    }
}
(Submitted by Luc Snels)
If you use the previous method with 16-bit char data (i.e. the upper 8 bits of a char are not zero) sustitute your "dataout.writeBytes()" with this "sendString()" method:
void sendString(String x){
	byte bytes[]=new byte[x.length()];
	x.getBytes(0,x.length(),bytes,0);
	try for (int i=0; i < x.length(); i++)
		dataout.writeByte(bytes[i]);
	catch(Exception e);
}

A related question...

"Also, whenever I run a CGI script that generates a document, no applets run from that document, presumably because the document directory changes, but I see no way of changing it back..."

Just make sure the CODEBASE attribute in the APPLET tag points to the directory in which the applet sits, then it works fine (either use an absolute path, or remember that the path will be relative to the directory the cgi-script resides in (usually /cgi-bin) ). - Ronals McNulty


How do I open a connection to a file on a server for reading and writing?



How do I retrieve an html page which is on an http server using authentication?

"This needs an encoded password and username (base64) and the ability to support the http authentication protocol extensions."

(Submitted by Ian Goh)
/***************************************************************************
   If you need to provide basic authentication information, you have to
   send the WWW Server the following line (at least!)

   Authorization: Basic auth_info

   where auth_info is the string "username:password" encoded in the
   Base64 Printable Encoding format.

   I rewrote the WWW "HTUU.C" encode function in Java.  I believe you
   do not need any other packages (except java.lang).

   Usage:

   String auth_info = HTUU.encode("username:password");

*****************************************************************************/



/*									 
** HTUU.CLASS
**
** ACKNOWLEDGEMENT:
**	Main code is taken from the HTUU.C distribution, and was originally
**	written by Mark Riordan (riordanmr@clvax1.cl.msu.edu)
** and Ari Luotonen (luotonen@dxcern.cern.ch).
**
** AUTHORS:
** IG  Ian Goh         ian.goh@jhu.edu
**
** HISTORY:
** Converted HTUU.C "HTUU_encode" function into Java (1.0.2): IG 13 July 1996
** -------------------------------------------------------------
**  File contains a routine to convert a buffer
**  of bytes to RFC 1113 printable encoding format.
**
**  This technique is similar to the familiar Unix uuencode
**  format in that it maps 6 binary bits to one ASCII
**  character (or more aptly, 3 binary bytes to 4 ASCII
**  characters).  However, RFC 1113 does not use the same
**  mapping to printable characters as uuencode.
**
**  Mark Riordan   12 August 1990 and 17 Feb 1991.
**  This code is hereby placed in the public domain.
** -------------------------------------------------------------
**
**
*/

class HTUU {

	static String version = "HTUU Class v1.0 7/13/96";
	
	// the Base64 printable encoding characters
	static char[] ENC = {
		'A','B','C','D','E','F','G','H','I','J','K','L','M',
		'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
		'a','b','c','d','e','f','g','h','i','j','k','l','m',
		'n','o','p','q','r','s','t','u','v','w','x','y','z',
		'0','1','2','3','4','5','6','7','8','9','+','/'
	};
		
	// function encode takes the "username:password" string and
	// converts it into the printable encoding format
			
	static String encode(String string) {
      
		int i, j;
		byte[] byte_array = new byte[3]; 
		StringBuffer buf_coded = new StringBuffer();

		// get length of input string
 		int nbytes = string.length();					
 
		for (i = 0; i < nbytes; i+= 3) {

			// check to make sure we don't run off the end of input string
			if (i + 3 < nbytes) 
				j = i + 3;
			else 
				j = nbytes;

			string.getBytes(i, j, byte_array, 0);		// get bytes i..j
		        
			if (j - i == 1) {
				// missing last two bytes
				byte_array[1] = 0;
				byte_array[2] = 0;
			}
         
			if (j - i == 2) {
				// missing last byte
				byte_array[2] = 0;
			}
         
			// convert the three bytes into four Base64 characters
			// and append to the buf_coded string buffer
			buf_coded.append(ENC[byte_array[0] >> 2]);           
			buf_coded.append(ENC[((byte_array[0] << 4) & 060) | ((byte_array[1] >> 4) & 017)]); 
			buf_coded.append(ENC[((byte_array[1] << 2) & 074) | ((byte_array[2] >> 6) & 03)]);
			buf_coded.append(ENC[byte_array[2] & 077]);

		} // end for loop

		// If nbytes was not a multiple of 3, then we have encoded too
		// many characters.  Adjust appropriately.

		int buf_length = buf_coded.length();
	
  		if (i == nbytes+1) {
     		/* There were only 2 bytes in that last group */
     		buf_coded.setCharAt(buf_length - 1, '=');
  		} else if (i == nbytes+2) {
     		/* There was only 1 byte in that last group */
     		buf_coded.setCharAt(buf_length - 1, '=');
     		buf_coded.setCharAt(buf_length - 2, '=');
  		}
           
		// return the Base64 encoded string
		return buf_coded.toString();
      
	} // end String encode (String)
   
} // end Class HTUU

How can Java be used to simulate HTML form submission?


(Submitted by Bill Giel)
Check out GUESTBOOK.JAVA, or GEODETIC.JAVA, both at http://www.nai.net/~rvdi/home.htm. Source code is at http://www.nai.net/~rvdi/guestbook.java (or geodetic.java).

How can I write a server-side CGI program in Java for handling a POST?


(Submitted by Pat Durante)
Click here to see Pat's code! (607 lines)
(Submitted by Fields Marshall)
Visit http://ogi.com:420/java for a documented example on how to do this ..

How do I implement a basic HTTP Server in Java?


(Submitted by Mark Friedman)
There is the Jigsaw HTTP server written in Java from the World Wide Web (W3) Consortium avaiable at http://www.w3.org/pub/WWW/Jigsaw/

Please note that I am not afilliated with the W3 Consortium. I am just reporting Jigsaw's existence.

(Submitted by Carl Davis)
There is a freeware HTTP server written in Java at: http://www.vpro.nl/interaktief/java/beta/httpd/sources.html.
(Submitted by Chuck Russell)
There is a commercially available HTTP server available in Java. ExpressO can be found at: http://www.capitalcity.com:4321. ExpressO can be purchased with our without a source code license.

How do I implement an image map with Java?


(Submitted by Cliff Berg)
Here is an example of a client-side image map program. (It is quickly hacked together, and I have not even compiled it, much less tried it; but it should work! Send me any comments. -CJB
/**
 * A Class to map rectangular image regions to a list of URL's.
 */

class MyURLList
{
	/**
	 * The rectangular areas
	 */

	Rectangle rects[] =
	{
		// Create two side-by-side rectangles
		new Rectangle(0, 0, 20, 20),
		new Rectangle(20, 0, 20, 20)
	}

	/**
	 * The  associated URL's
	 */

	URL urls[] =
	{
		// Associate a URL with each rectangular region
		new URL("http://www.somewhere.com/xyz.html"),
		new URL("http://www.anywhere.edu/~anybody/home.htm")
	}

	/**
	 * Method for performing the mapping
	 */

	public URL map(int x, int y)
	{
		for (int i = 0; i < rects.length; i++)
		{
			if (rects[i].inside(x, y)) return urls[i];
		}
		return null;
	}
}

/**
 * A canvas to server as the image to be mapped
 */

class MyCanvas extends Canvas
{
	MyURLList urlList;
	Applet applet;	// the (top level) applet loaded by the browser

	/**
	 * Construct the canvas
	 */

	public MyCanvas(MyURLList u, Applet a)
	{
		urlList = u;
		applet = a;
		resize(40, 20);
	}

	/**
	 * Catch image click events
	 */

	public boolean handleEvent(Event e)
	{
    	if (e.id == Event.MOUSE_DOWN)
    	{
    		// get the coordinates, and map them to a URL...
    		URL url = urlList.map(e.x, e.y);

    		// get the new HTML page
    		if (url != null) applet.getAppletContext().showDocument(url);

    		return true;
    	}
    	return super.handleEvent(e);            
	}
}

/**
 * The applet to be loaded by the browser
 */

public class MyApplet extends Applet
{
	public MyApplet()
	{
		MyURLList urlList = new MyURLList();
		add(new MyCanvas(urlList, this));
	}
}

How can an applet call a CGI script that is expecting a POST method AND display a returned HTML document in the browser?