/* * [Post.java] * * Summary: simulates a browser posting a form to CGI via POST. * * Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 2.0 2009-02-20 major refactoring. separate setParms and setPostParms. new send method. Post can have both types * of parm. * 2.1 2010-02-07 new methods Post.setBody Http.setRequestProperties. * 2.2 2010-04-05 new method getURL * 2.3 2010-11-14 new method setInstanceFollowRedirects */ package com.mindprod.http; import com.mindprod.common18.EIO; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.Charset; /** * 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 * @version 2.3 2010-11-14 new method setInstanceFollowRedirects * @since 1998 */ public final class Post extends Http { /** * parameters we send in the body of the message for a post. c.f. parms[] send on tail of command, * after a blank line after the URL. */ private String[] postParms; /** * the body of the Post message following the Post parms */ private String body = ""; /** * mime type of contents */ private String contentType = "application/x-www-form-urlencoded"; /** * Constructor */ public Post() { } /** * get the parms for the command encoded, separated with & =. no lead ? * * @param encoding encoding for URLEncoder * * @return all the parms in one string encoded without lead ? * @throws java.io.UnsupportedEncodingException if bad encoding */ String getEncodedPostParms( Charset encoding ) throws UnsupportedEncodingException { if ( postParms == null || postParms.length == 0 ) { return ""; } int estLength = 10; // allow a few slots for multibyte chars for ( String p : postParms ) { estLength += p.length() + 1; } final StringBuilder sb = new StringBuilder( estLength ); for ( int i = 0; i < postParms.length - 1; i += 2 ) { if ( i != 0 ) { sb.append( "&" ); } sb.append( URLEncoder.encode( postParms[ i ], encoding.name() ) ); sb.append( '=' ); sb.append( URLEncoder.encode( postParms[ i + 1 ], encoding.name() ) ); } return sb.toString(); } /** * Send a form full of data to the CGI host using POST * setPostParms must have been called previously, and possibly setParms as well. * * @param host domain of the website. no lead http: * @param port -1 if default, 8081 for local echoserver. * @param action action of form, page on website. Usually has a lead /. * @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 String send( String host, int port, String action, Charset encoding ) { try { init(); // URL will encode target and parms. URL url = new URI( "http", null, host, port, action, null, null ).toURL(); // these are the GET parms not the POST parms. Normally they are empty. final String encodedParms = getEncodedParms( encoding ); if ( encodedParms.length() > 0 ) { url = new URL( url.toString() + getEncodedParms( encoding ) ); } return send( url, encoding ); } catch ( URISyntaxException e ) { interruptResponseMessage = "Invalid URI/URL"; return null; } catch ( IOException e ) { interruptResponseMessage = e.getClass().getName() + " : " + e.getMessage(); return null; } } // end post /** * Send a form full of data to the CGI host using POST * Must have done a setParms(optional) and setPostParms beforehand. * * @param url URL of the website, including host, path but not the parms. * Call setPostParms and setPostBody before calling send. * http: or https: * @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", "WeakerAccess" } ) public String send( URL url, Charset encoding ) { try { init(); this.url = url; // urlc will contain subclasses of URLConnection like: // http: HttpURLConnection // https: HttpsURLConnectionImpl // file: FileURLConnection 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" ); setStandardProperties( urlc ); final byte[] encodedPostParms = getEncodedPostParms( encoding ).getBytes( EIO.UTF8 ); // could set Referrer: here final byte[] encodedBody = body.getBytes( encoding ); urlc.setRequestProperty( "Content-Length", Integer.toString( encodedPostParms.length + encodedBody.length ) /* in bytes */ ); urlc.setRequestProperty( "Content-Type", contentType ); // since post parms can be arbitrarily long. We have to connect first before sending any of it. urlc.connect(); // ignored if already connected. final OutputStream os = urlc.getOutputStream(); // parms are the data content. os.write( encodedPostParms ); os.write( encodedBody ); os.close(); return connectAndProcessResponse( encoding, urlc ); } catch ( ClassCastException e ) { // was not an http: url interruptResponseMessage = "Bug: not http/https: " + e.getMessage(); return null; } catch ( IOException e ) { interruptResponseMessage = e.getClass().getName() + " : " + e.getMessage(); return null; } } /** * declare Mime Type of the message contents. * Leave as default for sending post-style parms or encoded forms. * * @param contentType mime type of message */ public void setContentType( String contentType ) { this.contentType = contentType; } /** * set the body of the post, after the post parms. usually empty.. * * @param body text to form the body of the post. * * @see #setPostParms(String...) */ public void setPostBody( String body ) { if ( body == null ) { body = ""; } this.body = body; } /** * set the parms that will be send in the Post body. * * @param postParms 0..n strings to be send as parameter, alternating keyword/value, not encoded. * * @see Http#setParms(String...) */ public void setPostParms( String... postParms ) { if ( postParms == null ) { postParms = new String[ 0 ]; } assert ( postParms.length & 1 ) == 0 : "must have an even number of post parms, keyword=value"; this.postParms = postParms; } }