/*
* Copyright (C) 2000 - 2014 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://openbd.org/
* $Id: cfHttpConnection.java 2526 2015-02-26 15:58:34Z alan $
*/
package com.naryx.tagfusion.cfm.http;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.ContentEncodingHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.aw20.io.StreamUtil;
import org.aw20.util.StringUtil;
import com.nary.net.tagFilterReader;
import com.nary.net.http.urlEncoder;
import com.nary.net.http.urlResolver;
import com.naryx.tagfusion.cfm.engine.cfBinaryData;
import com.naryx.tagfusion.cfm.engine.cfBooleanData;
import com.naryx.tagfusion.cfm.engine.cfCatchData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfEngine;
import com.naryx.tagfusion.cfm.engine.cfJavaObjectData;
import com.naryx.tagfusion.cfm.engine.cfNumberData;
import com.naryx.tagfusion.cfm.engine.cfSession;
import com.naryx.tagfusion.cfm.engine.cfStringData;
import com.naryx.tagfusion.cfm.engine.cfStructData;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.file.fileDescriptor;
/*
* Created on each render of a CFHTTP tag
*/
@SuppressWarnings("deprecation")
public class cfHttpConnection implements cfHttpConnectionI {
private static Scheme https;
private static SchemeRegistry scheme;
static {
try {
X509HostnameVerifier hostVerifier = SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
if ( System.getProperty( "com.naryx.cfm.http.X509HostnameVerifier" ) != null ) {
String hostnameVerifier = System.getProperty( "com.naryx.cfm.http.X509HostnameVerifier" );
cfEngine.log( "-] Using alternative CFHTTP hostname verifier: " + hostnameVerifier );
hostVerifier = ( X509HostnameVerifier )Class.forName( hostnameVerifier ).newInstance();
}
SSLContext sslcontext = SSLContext.getDefault();
SSLSocketFactory socketFactory = new SSLSocketFactory( sslcontext, hostVerifier );
https = new Scheme( "https", 443, socketFactory );
scheme = new SchemeRegistry();
scheme.register( https );
} catch ( Exception e ) {
cfEngine.log( "-] Failed due to " + e.getClass().getName() + ": " + e.getMessage() );
}
}
private DefaultHttpClient client;
private HttpUriRequest message;
private boolean isMultipart;
private MultipartEntityBuilder multipartEntityBuilder = null;
public cfHttpConnection( cfSession _session, cfHttpData _httpData ) throws cfmRunTimeException {
this( _session, _httpData, null, null );
}
public cfHttpConnection( cfSession _session, cfHttpData _httpData, File _clientCert, String _clientPassword ) throws cfmRunTimeException {
init( _session, _httpData );
if ( _clientCert != null ) {
try {
client = getHttpClient( _clientCert, _clientPassword );
} catch ( Exception e ) {
throw newRunTimeException( "Failed to instantiate http client due to certificate issue. " + e.getClass().getName() + " was thrown: " + e.getMessage() );
}
} else {
client = new ContentEncodingHttpClient();
}
client.getParams().setBooleanParameter( ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true );
resolveLinks = false;
}
@Override
public void setMethod( String _method, boolean _multipart ) throws cfmRunTimeException {
message = resolveMethod( _method, _multipart );
isMultipart = _multipart;
}
@Override
public void setURL( String _url, int _port ) throws cfmRunTimeException {
port = _port;
url = _url;
if ( _url.startsWith( "/" ) ) {
url = "http://" + session.REQ.getServerName() + ":" + session.REQ.getServerPort() + session.REQ.getContextPath() + url;
}
try {
( ( HttpRequestBase )message ).setURI( new URI( url ) );
} catch ( URISyntaxException ue ) {
throw newRunTimeException( "Failed to set URL:" + ue.getReason() );
}
int uriPort = message.getURI().getPort();
if ( port == -1 && uriPort == -1 ) {
// use default port for http/https
} else if ( uriPort != -1 ) {
// use specified port
port = uriPort;
}
}
@Override
public void setProxyServer( String _proxyServer, int _proxyPort ) {
proxyServer = _proxyServer;
proxyPort = _proxyPort;
HttpHost proxy = new HttpHost( proxyServer, proxyPort );
client.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, proxy );
}
@Override
public void authenticate( String _user, String _password ) {
client.getCredentialsProvider().setCredentials( AuthScope.ANY, new UsernamePasswordCredentials( _user, _password ) );
}
@Override
public void authenticateProxy( String _user, String _password ) {
client.getCredentialsProvider().setCredentials( new AuthScope( proxyServer, proxyPort ), new UsernamePasswordCredentials( _user, _password ) );
}
@Override
public void setFollowRedirects( boolean _follow ) {
followRedirects = _follow;
client.getParams().setParameter( "http.protocol.handle-redirects", followRedirects );
}
@Override
public void setTimeout( int _timeout ) {
client.getParams().setParameter( "http.connection.timeout", _timeout );
client.getParams().setParameter( "http.socket.timeout", _timeout );
}
private DefaultHttpClient getHttpClient( File pKeyFile, String pKeyPassword ) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance( "SunX509" );
KeyStore keyStore = KeyStore.getInstance( "PKCS12" );
InputStream keyInput = null;
try {
keyInput = new FileInputStream( pKeyFile );
keyStore.load( keyInput, pKeyPassword.toCharArray() );
} finally {
if ( keyInput != null )
try {
keyInput.close();
} catch ( IOException ignored ) {
}
}
keyManagerFactory.init( keyStore, pKeyPassword.toCharArray() );
SSLSocketFactory sf = new SSLSocketFactory( keyStore, pKeyPassword );
sf.setHostnameVerifier( SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER );
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion( params, HttpVersion.HTTP_1_1 );
HttpProtocolParams.setContentCharset( params, HTTP.UTF_8 );
SchemeRegistry registry = new SchemeRegistry();
registry.register( new Scheme( "http", PlainSocketFactory.getSocketFactory(), 80 ) );
registry.register( new Scheme( "https", sf, 443 ) );
ClientConnectionManager ccm = new ThreadSafeClientConnManager( params, registry );
return new DefaultHttpClient( ccm, params );
}
@Override
public void connect() throws cfmRunTimeException {
// Collect up the request
addHeaders();
addCookies();
addURLData();
addFormData();
addFiles();
setBody();
setDefaultProxy();
// if we are building up a multipart then we should add this to the message
if ( multipartEntityBuilder != null ){
HttpEntity reqEntity = multipartEntityBuilder.build();
((HttpPost)message).setEntity( reqEntity );
}
// Execute the method.
int statusCode = -1;
int redirectLimit = 5;
HttpResponse response;
try {
client.getParams().setParameter( "http.protocol.max-redirects", redirectLimit );
response = client.execute( message );
statusCode = response.getStatusLine().getStatusCode();
} catch ( ConnectTimeoutException ce ) {
if ( !throwOnError ) {
handleError( "Connect Exception: Connection timed out.", "Connection Failed" );
} else {
throw newRunTimeException( "Connect Exception: Connection timed out." );
}
return;
} catch ( ClientProtocolException e ) {
if ( !throwOnError ) {
handleError( "Connect Exception: " + e.getMessage(), "Connection Failed" );
} else {
throw newRunTimeException( "Failed due to invalid Protocol: " + e.getMessage() );
}
return;
} catch ( IOException e ) {
if ( !throwOnError ) {
handleError( "Connect Exception: " + e.getMessage(), "Connection Failed" );
} else {
throw newRunTimeException( "Connect Exception: " + e.getMessage() );
}
return;
}
// if status code is -1
if ( statusCode != -1 )
handleResponse( response, statusCode );
}
private void setDefaultProxy() {
HttpHost currentProxy = ( HttpHost )client.getParams().getParameter( ConnRoutePNames.DEFAULT_PROXY );
if ( currentProxy == null ) {
ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner( client.getConnectionManager().getSchemeRegistry(), ProxySelector.getDefault() );
client.setRoutePlanner( routePlanner );
}
}
private void handleResponse( HttpResponse response, int _statusCode ) throws cfmRunTimeException {
// if the status code is not 2xx and throwOnError is true then throw an exception
if ( throwOnError && ( _statusCode < 200 || _statusCode > 299 ) ) {
throw newRunTimeException( _statusCode + " " + response.getStatusLine().getReasonPhrase() );
}
// Read response headers
Header[] respHeaders = response.getAllHeaders();
cfStructData responseHeaders = new cfStructData();
StringBuilder allHeaders = new StringBuilder();
String mimeType = "";
Header nextHeader;
// loop through the response headers creating the necessary cfStructData
// if there are any duplicate headers then an array of the values is created
for ( int i = 0; i < respHeaders.length; i++ ) {
nextHeader = respHeaders[i];
if ( responseHeaders.containsKey( nextHeader.getName() ) ) {
cfData headerVals = responseHeaders.getData( nextHeader.getName() );
if ( headerVals.getDataType() == cfData.CFSTRUCTDATA ) {
( ( cfStructData )headerVals ).setData( String.valueOf( ( ( cfStructData )headerVals ).size() + 1 ), new cfStringData( nextHeader.getValue() ) );
} else {
cfStructData newHeaderVals = new cfStructData();
newHeaderVals.setData( "1", headerVals );
newHeaderVals.setData( "2", new cfStringData( nextHeader.getValue() ) );
responseHeaders.setData( nextHeader.getName(), newHeaderVals );
}
} else {
responseHeaders.setData( nextHeader.getName(), new cfStringData( nextHeader.getValue() ) );
}
allHeaders.append( nextHeader );
if ( nextHeader.getName().toLowerCase().equals( "content-type" ) )
mimeType = nextHeader.getValue();
}
httpData.setData( "responseheader", responseHeaders );
StatusLine httpStatus = response.getStatusLine();
responseHeaders.setData( "HTTP_VERSION", new cfStringData( httpStatus.getProtocolVersion().toString() ) );
responseHeaders.setData( "EXPLANATION", new cfStringData( httpStatus.getReasonPhrase() ) );
responseHeaders.setData( "STATUS_CODE", new cfNumberData( _statusCode ) );
httpData.setData( "header", new cfStringData( httpStatus.toString() + " " + allHeaders.toString() ) );
httpData.setData( "mimetype", new cfStringData( mimeType ) );
int charsetIndex = mimeType.toLowerCase().indexOf( "charset" );
String charsetFromHeader = null;
if ( charsetIndex != -1 ) {
charsetFromHeader = mimeType.substring( charsetIndex + 8 ).trim();
httpData.setData( "charset", new cfStringData( charsetFromHeader ) );
} else {
httpData.setData( "charset", new cfStringData( "" ) );
}
byte[] responseBody = getResponseBody( response, outFile );
if ( responseBody == null ) {
throw newRunTimeException( "Failed to read response body." );
}
cfData fileContent = null;
byte resultType = getResultType( mimeType );
switch (resultType) {
case OBJECT:
if ( outFile == null ) {
java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
bos.write( responseBody, 0, responseBody.length );
fileContent = new cfJavaObjectData( bos );
break;
}
case BINARY:
if ( outFile == null )
fileContent = new cfBinaryData( responseBody );
else
fileContent = new cfStringData( new String( responseBody ) );
break;
case STRING:
String fileContentStr = getAsString( responseBody, charsetFromHeader );
if ( resolveLinks ) {
fileContentStr = resolveLinks( fileContentStr );
}
fileContent = new cfStringData( fileContentStr );
break;
}
httpData.setData( "filecontent", fileContent );
// if binary data then we ignore
if ( query != null && fileContent.getDataType() == cfData.CFSTRINGDATA ) {
handleQuery( fileContent.getString() );
}
httpData.setData( "statuscode", new cfStringData( _statusCode + " " + httpStatus.getReasonPhrase() ) );
httpData.setData( "errordetail", new cfStringData( "" ) );
httpData.setData( "text", fileContent.getDataType() == cfData.CFSTRINGDATA ? cfBooleanData.TRUE : cfBooleanData.FALSE );
cleanHttpData();
}
@Override
public void close() throws IOException {
client.getConnectionManager().shutdown();
}
// Private Methods
private HttpUriRequest resolveMethod( String _method, boolean _multipart ) throws cfmRunTimeException {
String method = _method.toUpperCase();
if ( method.equals( "GET" ) ) {
return new HttpGet();
} else if ( method.equals( "POST" ) ) {
return new HttpPost();
} else if ( method.equals( "HEAD" ) ) {
return new HttpHead();
} else if ( method.equals( "TRACE" ) ) {
return new HttpTrace();
} else if ( method.equals( "DELETE" ) ) {
return new HttpDelete();
} else if ( method.equals( "OPTIONS" ) ) {
return new HttpOptions();
} else if ( method.equals( "PUT" ) ) {
return new HttpPut();
}
throw newRunTimeException( "Unsupported METHOD value [" + method + "]. Valid METHOD values are GET, POST, HEAD, TRACE, DELETE, OPTIONS and PUT." );
}
private void addURLData() throws cfmRunTimeException {
addQueryStringData( httpData.getURLData(), httpData.getCharset() );
}
private void addQueryStringData( Map<String, String> _data, String _charset ) throws cfmRunTimeException {
if ( _data.size() > 0 ) { // don't need to do anything if there's no url data
// to add
StringBuilder queryString = new StringBuilder(); // method.getQueryString()
// );
Iterator<String> keys = _data.keySet().iterator();
while ( keys.hasNext() ) {
String nextKey = keys.next();
try {
queryString.append( urlEncoder.encode( nextKey, _charset ) );
} catch ( UnsupportedEncodingException e1 ) {
queryString.append( urlEncoder.encode( nextKey ) );
}
queryString.append( '=' );
try {
queryString.append( urlEncoder.encode( _data.get( nextKey ), _charset ) );
} catch ( UnsupportedEncodingException e ) {
queryString.append( urlEncoder.encode( _data.get( nextKey ) ) );
}
queryString.append( "&" );
}
// remove last &. We know there is at least one url param
queryString = queryString.deleteCharAt( queryString.length() - 1 );
String currentQStr = message.getURI().getQuery();
if ( currentQStr == null )
currentQStr = "";
try {
URI uri = message.getURI();
String schemeName = uri.getScheme();
String hostName = uri.getHost();
int port = uri.getPort();
String fragment = uri.getFragment();
String path = uri.getPath();
String queryStr = queryString.toString();
if ( currentQStr.length() > 0 ) {
uri = URIUtils.createURI( schemeName, hostName, port, path, currentQStr + '&' + queryStr, fragment );
} else {
uri = URIUtils.createURI( schemeName, hostName, port, path, queryStr, fragment );
}
( ( HttpRequestBase )message ).setURI( uri );
} catch ( URISyntaxException e ) {
throw newRunTimeException( "Failed due to URI Syntax Error: " + e.getMessage() );
}
}
}
private void addFormData() throws cfmRunTimeException {
Map<String, String> formData = httpData.getFormData();
Iterator<String> keys = formData.keySet().iterator();
String nextKey;
if ( message.getMethod().equalsIgnoreCase( "POST" ) ) {
if ( isMultipart || httpData.getFiles().size() > 0 ) {
if ( multipartEntityBuilder == null )
multipartEntityBuilder = MultipartEntityBuilder.create().setCharset( charset );
while ( keys.hasNext() ) {
nextKey = keys.next();
multipartEntityBuilder.addPart( nextKey, new StringBody( formData.get( nextKey ), ContentType.TEXT_PLAIN ) );
}
} else {
Header contentType = message.getFirstHeader( "Content-type" );
if ( contentType == null ) {
// otherwise it's been set manually so don't override it
message.setHeader( "Content-type", "application/x-www-form-urlencoded" );
}
StringBuilder paramStr = new StringBuilder();
while ( keys.hasNext() ) {
nextKey = keys.next();
paramStr.append( nextKey );
paramStr.append( '=' );
paramStr.append( formData.get( nextKey ) );
paramStr.append( '&' );
}
if ( paramStr.length() > 0 ) {
paramStr.deleteCharAt( paramStr.length() - 1 ); // remove last &
( ( HttpPost )message ).setEntity( new StringEntity( paramStr.toString(), this.charset ) );
}
}
}
}
private void addFiles() throws cfmRunTimeException {
List<fileDescriptor> files = httpData.getFiles();
if ( files.size() > 0 ) {
if ( message instanceof HttpPost && ( isMultipart || httpData.getFiles().size() > 0 ) ) {
if ( multipartEntityBuilder == null )
multipartEntityBuilder = MultipartEntityBuilder.create().setCharset( charset );
for ( int i = 0; i < files.size(); i++ ) {
fileDescriptor nextFile = files.get( i );
multipartEntityBuilder.addPart( nextFile.getName(), new FileBody( nextFile.getFile(), ContentType.create( nextFile.getMimeType() ), nextFile.getFile().getName() ) );
}
} else if ( message instanceof HttpPut ) {
fileDescriptor nextFile = files.get( 0 ); // just use the first file specified
try {
FileInputStream fileIn = new FileInputStream( nextFile.getFile() );
InputStreamEntity entity = new InputStreamEntity( fileIn, nextFile.getFile().length(), ContentType.create( nextFile.getMimeType() ) );
( ( HttpPut )message ).setEntity( entity );
} catch ( FileNotFoundException e ) {
throw newRunTimeException( "Failed to locate file " + nextFile.getFile().getAbsolutePath() );
}
}
}
}
/**
* Reads the response from the remote site
*
* @param _response
* @param gzip
* @param file
* @return
*/
private byte[] getResponseBody( HttpResponse _response, File file ) {
Header contentLenHdr = _response.getFirstHeader( "Content-Length" );
int expected = StringUtil.toInteger( (contentLenHdr != null ) ? contentLenHdr.getValue() : null, 4096 );
try {
if ( _response.getEntity() != null ) { // response body might be null if there is no body e.g. HEAD method used
InputStream in = _response.getEntity().getContent();
if ( file != null ) {
FileOutputStream out = new FileOutputStream( file );
try {
StreamUtil.copyTo( in, out );
} finally {
out.close();
}
return new String( file.getAbsolutePath() ).getBytes();
} else {
ByteArrayOutputStream out = new ByteArrayOutputStream( expected );
StreamUtil.copyTo( in, out );
return out.toByteArray();
}
} else {
return new byte[0];
}
} catch ( IOException ioe ) {
return null;
}
}
private void addHeaders() {
// There is no need to add HOST header since this is done by the
// "Jakarta Commons-HttpClient" HttpClient class.
message.addHeader( "Connection", "close" );
Map<String, String> headers = httpData.getHeaders();
Iterator<String> keys = headers.keySet().iterator();
boolean addUserAgent = true;
while ( keys.hasNext() ) {
String nextKey = keys.next();
message.addHeader( nextKey, headers.get( nextKey ) );
if ( nextKey.equalsIgnoreCase( "User-Agent" ) ) {
addUserAgent = false;
}
}
if ( addUserAgent )
message.addHeader( "User-agent", useragent );
// only set the content-type if it hasn't already been set, thus allowing for it to be overridden
if ( httpData.isBodySet() && message.getLastHeader( "Content-type" ) == null )
message.setHeader( "Content-type", httpData.getContentType() );
}
private void addCookies() {
Map<String, List<String>> cookies = httpData.getCookies();
Iterator<String> keys = cookies.keySet().iterator();
String domain = "";
domain = message.getURI().getHost();
CookieStore cookieStore = new BasicCookieStore();
HttpContext localContext = new BasicHttpContext();
// Bind custom cookie store to the local context
localContext.setAttribute( ClientContext.COOKIE_STORE, cookieStore );
client.getParams().setParameter( ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY );
while ( keys.hasNext() ) {
String nextKey = keys.next();
List<String> values = cookies.get( nextKey );
Date date = new Date( 2038, 1, 1 );
for ( int i = 0; i < values.size(); i++ ) {
BasicClientCookie cookie = new BasicClientCookie( nextKey, values.get( i ) );
cookieStore.addCookie( cookie );
cookie.setVersion( 1 );
cookie.setDomain( domain );
cookie.setPath( "/" );
cookie.setExpiryDate( date );
cookie.setSecure( false );
}
}
client.setCookieStore( cookieStore );
}
public void setBody() throws cfmRunTimeException {
// if there is a request body set and this is a PUT or POST
if ( httpData.isBodySet() ) {
try {
if ( message instanceof HttpPost ) {
if ( this.charset == null ) {
( (HttpPost) message ).setEntity( new StringEntity( httpData.getBody() ) );
} else {
( (HttpPost) message ).setEntity( new StringEntity( httpData.getBody(), this.charset ) );
}
} else if ( message instanceof HttpPut ) {
if ( this.charset == null ) {
( (HttpPut) message ).setEntity( new StringEntity( httpData.getBody() ) );
} else {
( (HttpPut) message ).setEntity( new StringEntity( httpData.getBody(), this.charset ) );
}
}
} catch ( UnsupportedEncodingException | UnsupportedCharsetException e ) {
throw newRunTimeException( "Failed due to UnsupportedEncoding while setting body: " + e.getMessage() );
}
}
}
protected String url;
protected int port;
protected Charset charset;
protected boolean resolveLinks;
protected boolean followRedirects;
protected String useragent;
protected cfSession session;
protected boolean throwOnError;
protected String getAsBinary;
protected File outFile = null;
protected queryDetails query = null;
protected cfHttpData httpData;
protected String proxyServer;
protected int proxyPort;
protected final static byte STRING = 0, BINARY = 1, OBJECT = 2;
protected void init( cfSession _session, cfHttpData _httpData ) {
session = _session;
httpData = _httpData;
}
protected void handleQuery( String _page ) throws cfmRunTimeException {
if ( query == null )
return; // shouldn't happen but check anyway
if ( query.columns == null && _page.trim().length() == 0 ) {
throw newRunTimeException( "Cannot create Query. Empty file returned in HTTP call with no COLUMNS specified." );
}
if ( query.name.length() == 0 ) { // no need to create query with no name
return;
}
if ( query.delimiter.length() != 1 ) {
throw newRunTimeException( "Invalid delimiter value. Delimiter must be one character in length." );
}
char delimiter = query.delimiter.charAt( 0 );
if ( query.textqualifier.length() > 1 ) {
throw newRunTimeException( "Invalid textQualifier set. TextQualifier must be 0 or 1 character in length." );
}
// get columns and create the query
// columns are given in the tag as COLUMNS parameter
new cfHttpQueryData( session, _page, query.columns, query.name, delimiter, query.textqualifier, false, query.firstRowAsHeaders );
}
protected void saveToFile( cfData _filecontent ) throws cfmRunTimeException {
try {
FileOutputStream fout = new FileOutputStream( outFile );
if ( _filecontent.getDataType() == cfData.CFSTRINGDATA ) {
fout.write( _filecontent.getString().getBytes() );
} else if ( _filecontent.getDataType() == cfData.CFBINARYDATA ) {
fout.write( ( ( cfBinaryData )_filecontent ).getByteArray() );
}
fout.flush();
fout.close();
} catch ( IOException ioe ) {
throw newRunTimeException( "Failed to save filecontent to file. IOException: " + ioe );
}
}
protected String resolveLinks( String _content ) {
tagFilterReader reader = new tagFilterReader( new StringReader( _content ) );
reader.registerTagFilter( new urlResolver( url, port ) );
java.io.StringWriter writer = new java.io.StringWriter();
try {
int ch = reader.readChar();
while ( ch != -1 ) {
writer.write( ch );
ch = reader.readChar();
}
return writer.toString();
} catch ( IOException ignored ) {
} // won't happen since the source is a String
return _content;
}
protected cfmRunTimeException newRunTimeException( String _msg ) {
cfCatchData catchData = new cfCatchData( session );
catchData.setMessage( _msg );
return new cfmRunTimeException( catchData );
}
private class queryDetails {
String name;
String columns;
String textqualifier;
String delimiter;
boolean firstRowAsHeaders;
public queryDetails( String _name, String _cols, String _tq, String _delim, boolean _first ) {
name = _name;
columns = _cols;
textqualifier = _tq;
delimiter = _delim;
firstRowAsHeaders = _first;
}
}
@Override
public void setQueryDetails( String _name, String _cols, String _textqualifier, String _delimiter, boolean _first ) {
query = new queryDetails( _name, _cols, _textqualifier, _delimiter, _first );
}
@Override
public void setThrowOnError( boolean b ) {
throwOnError = b;
}
@Override
public void setFile( File _out ) {
outFile = _out;
}
@Override
public void setCharset( String _charset ) throws cfmRunTimeException {
try {
charset = Charset.forName( _charset );
} catch ( java.nio.charset.IllegalCharsetNameException e ) {
throw newRunTimeException( "Invalid CHARSET value specified: " + _charset + "." );
}
}
@Override
public void setUserAgent( String _ua ) {
useragent = _ua;
}
protected void cleanHttpData() {
// remove headers, data, files
httpData.removeData();
httpData.removeHeaders();
httpData.removeFiles();
httpData.removeCookies();
}
protected static boolean isText( String _contentType ) {
if ( _contentType.equals( "" ) || _contentType.startsWith( "text" ) || _contentType.startsWith( "message" ) || _contentType.equals( "application/octet-stream" ) || _contentType.equals( "application/xml" ) || _contentType.equals( "application/json" ) ) {
return true;
} else {
return false;
}
}
protected void handleError( String _error, String _content ) {
httpData.setData( "charset", new cfStringData( "" ) );
httpData.setData( "errordetail", new cfStringData( _error ) );
httpData.setData( "filecontent", new cfStringData( _content ) );
httpData.setData( "header", new cfStructData() );
httpData.setData( "mimetype", new cfStringData( "Unable to determine MIME type of file" ) );
httpData.setData( "responseheader", new cfStructData() );
httpData.setData( "statuscode", new cfStringData( "Status code unavailable" ) );
httpData.setData( "text", cfBooleanData.TRUE );
}
protected byte getResultType( String _contentType ) throws cfmRunTimeException {
if ( getAsBinary.equals( "yes" ) ) {
return BINARY;
} else if ( getAsBinary.equals( "no" ) ) {
if ( isText( cleanContentType( _contentType ) ) ) {
return STRING;
} else {
return OBJECT;
}
} else if ( getAsBinary.equals( "auto" ) ) {
if ( isText( cleanContentType( _contentType ) ) ) {
return STRING;
} else {
return BINARY;
}
} else {
throw newRunTimeException( "Invalid GETASBINARY value specified: " + getAsBinary + ". Supported values are NO, YES and AUTO." );
}
}
private static String cleanContentType( String _contentType ) {
int semiColonIndx = _contentType.indexOf( ';' );
if ( semiColonIndx != -1 ) {
return _contentType.substring( 0, semiColonIndx );
}
return _contentType;
}
@Override
public void setGetAsBinary( String _gab ) {
getAsBinary = _gab;
}
@Override
public void setResolveLinks( boolean _resolve ) {
resolveLinks = _resolve;
}
private String getAsString( byte[] _body, String _charsetFromHeader ) throws cfmRunTimeException {
if ( _body == null ) {
return "";
}
String useCharset;
if ( charset == null ) {
useCharset = _charsetFromHeader;
} else {
useCharset = charset.toString();
}
String fileContentStr;
if ( useCharset != null ) {
try {
fileContentStr = new String( _body, com.nary.util.Localization.convertCharSetToCharEncoding( useCharset ) );
} catch ( UnsupportedEncodingException ue ) {
throw newRunTimeException( "Unsupported charset specified: " + useCharset );
}
} else {
fileContentStr = new String( _body );
}
return fileContentStr;
}
}