package Acme;
import java.net.*;
import java.io.*;
import java.util.*;
import java.text.*;
/// A URLConnection that implements POST.
// <P>
// Some implementations of URLConnection, e.g. the one in Navigator 3.0,
// do not support POST. This is a stripped-down version that does.
// <P>
// Note that it can't inherit from java.net.URLConnection because that
// class has no public constructors. Not all the standard URLConnection
// methods are re-implemented here, just the ones necessary for posting.
// <P>
// This class is not needed in current browsers.
// <P>
// <A HREF="/resources/classes/Acme/PostURLConnection.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
public class PostURLConnection
{
private URL url;
private boolean doInput = false;
private boolean doOutput = true;
private boolean useCaches = false;
private Vector reqHeaderNames = new Vector();
private Vector reqHeaderValues = new Vector();
private Vector resHeaderNames = null;
private Vector resHeaderValues = null;
private Socket socket;
private OutputStream out;
private InputStream in;
/// Constructs a POST URL connection to the specified URL.
// @param url the specified URL
public PostURLConnection( URL url )
{
this.url = url;
}
private boolean connected = false;
public void connect() throws IOException
{
if ( connected )
return;
if ( ! useCaches )
setRequestProperty( "Pragma", "no-cache" );
String protocol = url.getProtocol();
if ( ! protocol.equals( "http" ) )
throw new UnknownServiceException( "unknown protocol" );
String host = url.getHost();
int port = url.getPort();
if ( port == -1 )
port = 80;
String file = url.getFile();
socket = new Socket( host, port );
out = socket.getOutputStream();
PrintStream pout = new PrintStream( out );
String method;
if ( doOutput )
method = "POST";
else
method = "GET";
pout.println( method + " " + file + " HTTP/1.0" );
for ( int i = 0; i < reqHeaderNames.size(); ++i )
{
String name = (String) reqHeaderNames.elementAt( i );
String value = (String) reqHeaderValues.elementAt( i );
pout.println( name + ": " + value );
}
pout.println( "" );
pout.flush();
connected = true;
}
private boolean inputStarted = false;
private void startInput() throws IOException
{
connect();
if ( inputStarted )
return;
in = socket.getInputStream();
resHeaderNames = new Vector();
resHeaderValues = new Vector();
DataInputStream din = new DataInputStream( in );
String line;
// Read and ignore the status line.
line = din.readLine();
// Read and save the header lines.
while ( true )
{
line = din.readLine();
if ( line == null || line.length() == 0 )
break;
int colonBlank = line.indexOf( ": " );
if ( colonBlank != -1 )
{
String name = line.substring( 0, colonBlank );
String value = line.substring( colonBlank + 2 );
resHeaderNames.addElement( name.toLowerCase() );
resHeaderValues.addElement( value );
}
}
inputStarted = true;
}
public void close() throws IOException
{
if ( ! connected )
return;
out.close();
if ( inputStarted )
in.close();
socket.close();
}
/// Gets the URL for this connection.
public URL getURL()
{
return url;
}
// Gets the content length. Returns -1 if not known.
public int getContentLength() throws IOException
{
return getHeaderFieldInt( "content-length", -1 );
}
/// Gets the content type. Returns null if not known.
public String getContentType() throws IOException
{
return getHeaderField( "content-type" );
}
/// Gets a header field by name. Returns null if not known.
// @param name the name of the header field
public String getHeaderField( String name ) throws IOException
{
if ( resHeaderNames == null )
startInput();
int i = resHeaderNames.indexOf( name.toLowerCase() );
if ( i == -1 )
return null;
return (String) resHeaderValues.elementAt( i );
}
/// Gets a header field by name. Returns null if not known.
// The field is parsed as an integer.
// @param name the name of the header field
// @param def the value to return if the field is missing or malformed.
public int getHeaderFieldInt( String name, int def ) throws IOException
{
try
{
return Integer.parseInt( getHeaderField( name ) );
}
catch ( NumberFormatException t )
{
return def;
}
}
/// Gets a header field by name. Returns null if not known.
// The field is parsed as a date.
// @param name the name of the header field
// @param def the value to return if the field is missing or malformed.
public long getHeaderFieldDate( String name, long def ) throws IOException
{
try
{
return DateFormat.getDateInstance().parse( getHeaderField( name ) ).getTime();
}
catch ( ParseException e )
{
throw new IOException( e.toString() );
}
}
/// Call this routine to get an InputStream that reads from the object.
// @exception UnknownServiceException If the protocol does not support input.
public InputStream getInputStream() throws IOException
{
if ( ! doInput )
throw new UnknownServiceException(
"connection doesn't support input" );
startInput();
return in;
}
/// Call this routine to get an OutputStream that writes to the object.
// @exception UnknownServiceException If the protocol does not support output.
public OutputStream getOutputStream() throws IOException
{
if ( ! doOutput )
throw new UnknownServiceException(
"connection doesn't support output" );
connect();
return out;
}
/// Returns the String representation of the URL connection.
public String toString()
{
return this.getClass().getName() + ":" + url;
}
/// A URL connection can be used for input and/or output. Set the DoInput
// flag to true if you intend to use the URL connection for input,
// false if not. The default for PostURLConnections is false.
public void setDoInput( boolean doInput )
{
if ( connected )
throw new IllegalAccessError( "already connected" );
this.doInput = doInput;
}
public boolean getDoInput()
{
return doInput;
}
/// A URL connection can be used for input and/or output. Set the DoOutput
// flag to true if you intend to use the URL connection for output,
// false if not. The default for PostURLConnections is true.
public void setDoOutput( boolean doOutput )
{
if ( connected )
throw new IllegalAccessError( "already connected" );
this.doOutput = doOutput;
}
public boolean getDoOutput()
{
return doOutput;
}
/// Some protocols do caching of documents. Occasionally, it is important
// to be able to "tunnel through" and ignore the caches (e.g. the "reload"
// button in a browser). If the UseCaches flag on a connection is true,
// the connection is allowed to use whatever caches it can. If false,
// caches are to be ignored. The default for PostURLConnections is false.
public void setUseCaches( boolean useCaches )
{
if ( connected )
throw new IllegalAccessError( "already connected" );
this.useCaches = useCaches;
}
public boolean getUseCaches()
{
return useCaches;
}
// Sets/gets a general request property.
// @param name The keyword by which the request is known (eg "accept")
// @param value The value associated with it.
public void setRequestProperty( String name, String value )
{
if ( connected )
throw new IllegalAccessError("already connected");
reqHeaderNames.addElement( name );
reqHeaderValues.addElement( value );
}
public String getRequestProperty( String name )
{
if ( connected )
throw new IllegalAccessError( "already connected" );
int i = reqHeaderNames.indexOf( name );
if ( i == -1 )
return null;
return (String) reqHeaderValues.elementAt( i );
}
}