// PostURLConnection - a URLConnection that implements POST // // Copyright (C)1996,1998 by Jef Poskanzer . All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. package Acme; import java.net.*; import java.io.*; import java.util.*; import java.text.*; /// A URLConnection that implements POST. //

// Some implementations of URLConnection, e.g. the one in Navigator 3.0, // do not support POST. This is a stripped-down version that does. //

// 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. //

// This class is not needed in current browsers. //

// Fetch the software.
// Fetch the entire Acme package. 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 ); } }