// copyright (c) 1998-2005 Roedy Green, Canadian Mind Products
// based on work by Jonathan Revusky

// To encode strings use java.net.URLEncoder.encode;
// and Java.net.URLDecoder.decode or Request.

/*
 * copyright (c) 1998-2005
 * Roedy Green
 * Canadian Mind Products
 * #327 - 964 Heywood Avenue
 * Victoria, BC Canada V8V 2Y5
 * tel: (250) 361-9093
 * roedy g at mindprod dotcom
 * http://mindprod.com
 * version history:
 * 1.3 2007-08-24 readStringBlocking, readBytesBlocking, encoding on Post
 * 1.4 2007-09-26 add TIMEOUT
 * 1.5 2007-12-30 add alternate get and post methods that take a full URL.
 * 1.6 2008-01-14 add gzip option on read
 */
package com.mindprod.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.*;

/**
 * simulates a browser posting a form to CGI via POST. See com.mindprod.submitter for sample code to use this class.
 *
 * @author Roedy Green, Canadian Mind Products may be copied and used freely for any purpose but military.
 * @version 1.6 2008-01-14 add gzip option on read
 */
public final class Post
    {
// ------------------------------ FIELDS ------------------------------

    /**
     * Allow 50 seconds to connect
     */
    private static final int CONNECT_TIMEOUT = 50 * 1000;

    /**
     * Allow 40 seconds for a read to go without progress
     */
    private static final int READ_TIMEOUT = 40 * 1000;

    /**
     * responseCode from most recent post
     */
    private static int responseCode;

    /**
     * 40 seconds TIMEOUT = 40,000 ms.
     */
    private static final int TIMEOUT = 40000;
// -------------------------- PUBLIC STATIC METHODS --------------------------

    /**
     * responseCode from most recent post
     *
     * @return responseCode
     */
    public static int getResponseCode()
        {
        return responseCode;
        }

    /**
     * Send a formful of data to the CGI host using POST
     *
     * @param url      URL of the website, including host, path but not the parms.
     * @param parms    alternating parm value without = or ? not urlEncoded.
     * @param encoding encoding of the byte stream result, usually UTF-8 or or ISO-8859-1.
     *
     * @return CGI host's response with headers and embedded length fields stripped
     */
    @SuppressWarnings({ "UnusedAssignment", "MethodNamesDifferingOnlyByCase" })
    public static String post( URL url,
                               String encoding,
                               String... parms )
        {
        try
            {
            // prepare parms
            final StringBuilder sb = new StringBuilder( 200 );
            for ( int i = 0; i < parms.length - 1; i += 2 )
                {
                if ( i != 0 )
                    {
                    sb.append( "&" );
                    }
                sb.append( URLEncoder.encode( parms[ i ], "UTF-8"
                                              /* encoding */ ) );
                sb.append( '=' );
                sb.append( URLEncoder.encode( parms[ i + 1 ], "UTF-8"
                                              /* encoding */ ) );
                }

            final byte[] encodedParms =
                    sb.toString().getBytes( "UTF-8"/* encoding */ );

            final HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
            urlc.setAllowUserInteraction( false );
            urlc.setDoInput( true );
            urlc.setDoOutput( true );// parms at the tail of this
            urlc.setUseCaches( false );
            urlc.setRequestMethod( "POST" );
            urlc.setConnectTimeout( CONNECT_TIMEOUT );
            urlc.setReadTimeout( READ_TIMEOUT );
            // could set Referer: here
            urlc.setRequestProperty( "Content-Length",
                                     Integer.toString( encodedParms.length )
                                     /* in bytes */ );
            urlc.setRequestProperty( "Content-Type",
                                     "application/x-www-form-urlencoded" );

            urlc.connect();

            final OutputStream os = urlc.getOutputStream();
            // parms are the data content.
            os.write( encodedParms );
            os.close();
            /* save responseCode for later retrieval */
            responseCode = urlc.getResponseCode();

            // get size of message. -1 means comes in an indeterminate number of chunks.
            int estimatedLength = urlc.getContentLength();
            if ( estimatedLength < 0 )
                {
                // quite common for no length field
                estimatedLength = 32 * 1024;
                }
            // R E A D
            final InputStream is = urlc.getInputStream();

            // content encoding might be null
            final boolean gzipped = "gzip".equals( urlc.getContentEncoding() );

            String result = Read.readStringBlocking( is,
                                                     estimatedLength,
                                                     TIMEOUT,
                                                     gzipped,
                                                     encoding );

            // C L O S E
            is.close();
            urlc.disconnect();
            return result;
            }
        catch ( IOException e )
            {
            return null;
            }
        }

    /**
     * Send a formful of data to the CGI host using POST
     *
     * @param host     URL of the website
     * @param action   action of form, page on website. Usually has a lead /.
     * @param parms    alternating parm value without = or ? not urlEncoded.
     * @param port     -1 if default, 8081 for local echoserver.
     * @param encoding encoding of the byte stream result, usually UTF-8 or or ISO-8859-1.
     *
     * @return CGI host's response with headers and embedded length fields stripped
     */
    @SuppressWarnings({ "UnusedAssignment", "MethodNamesDifferingOnlyByCase" })
    public static String post( String host,
                               int port,
                               String encoding,
                               String action,
                               String... parms )
        {
        try
            {
            // URL will encode target and parms.
            URL url = new URI( "http",
                               null,
                               host,
                               port,
                               action,
                               null,
                               null ).toURL();

            return post( url, encoding, parms );
            }
        catch ( URISyntaxException e )
            {
            return null;
            }
        catch ( IOException e )
            {
            return null;
            }
        }// end post

// --------------------------- CONSTRUCTORS ---------------------------

    /**
     * Static only.  Prevent instantiation.
     */
    private Post()
        {
        }
    }