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