All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.boot.transport.HTTPTransport Maven / Gradle / Ivy

There is a newer version: 1.16.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

/*
 * Created on 16.05.2003
 */
package at.spardat.xma.boot.transport;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.SimpleTimeZone;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;

import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.cookie.CookieSpec;
import org.apache.commons.httpclient.cookie.CookieSpecBase;
import org.apache.commons.httpclient.cookie.MalformedCookieException;

import at.spardat.xma.boot.BootRuntime;
import at.spardat.xma.boot.Statics;
import at.spardat.xma.boot.comp.CCLoader;
import at.spardat.xma.boot.component.IRtXMASessionClient;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.boot.logger.Logger;

/**
 * This class implements low level transport on http transport using suns UrlConnection implementation.
 *
 * @author s2877, s3595
 * @version $Id: HTTPTransport.java 10965 2013-08-22 11:23:48Z dschwarz $
 *
 */
public class HTTPTransport extends Transport {

    /** used to format if-modified-since correctly */
    private static DateFormat httpdate_;

    private static Logger log_;

    private static HostnameVerifier hostnameVerifier;

    private static CookieSpec cookieSpec;
    private static HttpState httpState = new HttpState();

    private HashMap redirectCache = new HashMap();
    
    {
       httpdate_ = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US );            //$NON-NLS-1$
       httpdate_.setTimeZone(new SimpleTimeZone(0, "GMT")); //$NON-NLS-1$
    }

    /**
     * Initializes the underlaying http-protocol-provider from the given Propterties.
     * This sets the tcp-timeouts, the proxy-settings and ssl-settings.
     * @param prop containing properties for http and https protocol
     */
    public static void init( Properties prop ){

        log_ = Logger.getLogger( "boot.transport.http" ); //$NON-NLS-1$

        // proxy properties
        String strProxyEnable   = prop.getProperty( Statics.CFG_PROP_PROXYENABLE );
        if( strProxyEnable != null && Boolean.valueOf(strProxyEnable).booleanValue()) {
            String strProxyServer   = prop.getProperty( Statics.CFG_PROP_PROXYSERVER );
            String strProxyPort     = prop.getProperty( Statics.CFG_PROP_PROXYPORT );
            if( strProxyServer!=null &&  strProxyPort!=null ) {
                System.setProperty( "proxySet", "true" );
                System.setProperty( "http.proxyHost", strProxyServer );
                System.setProperty( "http.proxyPort", strProxyPort  );
                log_.log(LogLevel.FINE, "transport proxy is: {0}:{1}", new Object[] { strProxyServer, strProxyPort } );
            }
            String strSecureProxyServer   = prop.getProperty( Statics.CFG_PROP_SECUREPROXYSERVER );
            String strSecureProxyPort     = prop.getProperty( Statics.CFG_PROP_SECUREPROXYPORT );
            if(strSecureProxyPort!=null&&strSecureProxyServer!=null) {
                System.setProperty("https.proxyHost",strSecureProxyServer);
                System.setProperty("https.proxyPort",strSecureProxyPort);
                log_.log(LogLevel.FINE, "secure transport proxy is: {0}:{1}", new Object[] { strSecureProxyServer, strSecureProxyPort } );
            }
            String strProxyOverride = prop.getProperty( Statics.CFG_PROP_PROXYOVERRIDE );
            if(strProxyOverride!=null) {
                strProxyOverride = strProxyOverride.replace(';','|'); // documented delimiter for IE
                strProxyOverride = strProxyOverride.replace(',','|'); // IE supports ',' as delimiter, too
                strProxyOverride = strProxyOverride.replace(' ','|'); // IE supports blank as delimiter, too
                strProxyOverride = strProxyOverride.replace('\t','|'); // IE supports tab as delimiter, too
                strProxyOverride = strProxyOverride.replace('\r','|'); // IE supports carriage return as delimiter, too
                strProxyOverride = strProxyOverride.replace('\n','|'); // IE supports newline as delimiter, too
                strProxyOverride = strProxyOverride.replaceAll("","localhost|127.0.0.1");
                System.setProperty("http.nonProxyHosts",strProxyOverride);
                log_.log(LogLevel.FINE, "proxy not used for: {0}", strProxyOverride );
            }
        } else {
            log_.log(LogLevel.FINE, "no transport proxy is used" );
        }

        // timeout properties
        String strConnectTimeout = prop.getProperty(Statics.CFG_PROP_CONNECTTIMEOUT);
        if(strConnectTimeout!=null) {
            System.setProperty("sun.net.client.defaultConnectTimeout",strConnectTimeout);
            log_.log(LogLevel.FINE, "http connect timeout: "+strConnectTimeout+" milliseconds");
        }
        String strReadTimeout = prop.getProperty(Statics.CFG_PROP_READTIMEOUT);
        if(strReadTimeout!=null) {
            System.setProperty("sun.net.client.defaultReadTimeout",strReadTimeout);
            log_.log(LogLevel.FINE, "http read timeout: "+strReadTimeout+" milliseconds");
        }

        // ssl properties
        String strTrustStore = prop.getProperty(Statics.CFG_PROP_SECURECERTS);
        if(strTrustStore!=null) {
            log_.log(LogLevel.FINE,"using trusted certificates file "+strTrustStore);
            File trustFile = new File(strTrustStore);
            if(!trustFile.exists()) {
                log_.log(LogLevel.SEVERE,"trusted certificates file '"+trustFile.getAbsolutePath()+"' not found");
            }
            System.setProperty("javax.net.ssl.trustStore",strTrustStore);
        }

        hostnameVerifier = new HostnameVerifierImpl(prop.getProperty(Statics.CFG_PROP_HOSTNAMEVERIFYIGNORE));

        // cookie handling policy
        Class cookiePolicyClass = null;
        String strCookiePolicy = prop.getProperty(Statics.CFG_PROP_COOKIEPOLICY);
        if(strCookiePolicy!=null) {
            try {
               cookiePolicyClass = Class.forName(strCookiePolicy);
            } catch (ClassNotFoundException e) {
                log_.log(LogLevel.WARNING,"configured cookiePolicy '"+strCookiePolicy+"' not found, using default");
            }
        }
        if(cookiePolicyClass==null) {
            cookiePolicyClass = CookieSpecBase.class;
        }
        log_.log(LogLevel.FINE,"using cookiePolicy "+cookiePolicyClass.getName());
        CookiePolicy.registerCookieSpec(CookiePolicy.DEFAULT, cookiePolicyClass);
        cookieSpec=CookiePolicy.getDefaultSpec();
    }

    /**
     * logs the proxy settings at the given level.
     */
    public void logProxyInfo(LogLevel level) {
        StringBuffer result = new StringBuffer();
        String proxyHost = System.getProperty("http.proxyHost");
        if(proxyHost!=null) {
            result.append("http proxy is: "+proxyHost);
            String proxyPort = System.getProperty("http.proxyPort");
            if(proxyPort!=null) {
                result.append(":"+proxyPort);
            }
            log_.log(level,result.toString());
        } else {
            log_.log(level,"no http proxy used");
        }
        result = new StringBuffer();
        proxyHost = System.getProperty("https.proxyHost");
        if(proxyHost!=null) {
            result.append("https proxy is: "+proxyHost);
            String proxyPort = System.getProperty("https.proxyPort");
            if(proxyPort!=null) {
                result.append(":"+proxyPort);
            }
            log_.log(level,result.toString());
        } else {
            log_.log(level,"no https proxy used");
        }
        String nonProxyHosts = System.getProperty("http.nonProxyHosts");
        if(nonProxyHosts!=null) {
            log_.log(level,"no proxy used for: "+nonProxyHosts);
        }
    }

    /**
     * Constructs a HTTPTransport.
     *
     */
    public HTTPTransport() {
        if( log_==null)
            log_ = Logger.getLogger( "boot.httpTransport" ); //$NON-NLS-1$
    }

    /**
     * Converts a java.util.Date to a String using the encoding specified by
     * the HTTP-Specification. We have to wrap this task, cause we may have to change it. 

* see also SUN-Bug_ID: 4397096

* * @param date the date to be formated * @return String the formated representation */ public static String httpDate(Date date) { return httpdate_.format(date); } /** * Converts a long containing a date to a String using the encoding specified by * the HTTP-Specification. We have to wrap this task, cause we may have to change it.

* see also SUN-Bug_ID: 4397096

* * @param ldate the milliseconds since January 1, 1970, 00:00:00 GMT. * @return String the formated representation */ public static String httpDate(long ldate ) { return httpdate_.format( new Date(ldate)); } /* (non-Javadoc) * @see at.spardat.xma.transport.Transport#getResource(at.spardat.xma.transport.XMA_URI, long) */ public Result getResource(final IRtXMASessionClient session, final XMA_URI resource, final long modifiedSince, final String etag) throws CommunicationException { CCLoader swtCLoader = BootRuntime.getInstance().getAppManager().getSWTClassLoader(); if(swtCLoader!=null) { class Inner implements Runnable { Result result; CommunicationException exc; public void run() { try { result = getResourceImpl(session,resource,modifiedSince,etag); } catch(CommunicationException exc) { this.exc=exc; } } } Inner inner = new Inner(); try { // call BusyIndicator.showWhile over reflection from the SWT-Classloader Class busyIndicatorClass = swtCLoader.loadClass("org.eclipse.swt.custom.BusyIndicator"); Class displayClass = swtCLoader.loadClass("org.eclipse.swt.widgets.Display"); Method method = busyIndicatorClass.getMethod("showWhile",new Class[]{displayClass,Runnable.class}); method.invoke(null,new Object[]{null,inner}); } catch (Exception exc) { throw new RuntimeException(exc); } if(inner.exc!=null) throw inner.exc; return inner.result; } else { return getResourceImpl(session,resource,modifiedSince,etag); } } /* (non-Javadoc) * @see at.spardat.xma.boot.transport.Transport#getRedirection(at.spardat.xma.boot.transport.XMA_URI) */ @Override public XMA_URI getRedirection(XMA_URI resource) { String resourceHostApp = resource.getHostApp(); String translated = redirectCache.get(resourceHostApp); if (translated != null) { String newUrl = resource.toString().replace(resourceHostApp, translated); try { return new XMA_URI(newUrl); } catch (MalformedURLException e) { log_.info("Can't translate URL: " + newUrl + ": " + e.toString()); } } return null; } private Object callRedirectAware(XMA_URI resource, RedirectCallback callback) throws CommunicationException { Set redirectLoopPreventionSet = new HashSet(); URL url = resource.getHTTP_URI(); String initialResourceHostApp = resource.getHostApp(); do { XMA_URI redirectedResource = getRedirection(resource); if (redirectedResource != null) { url = redirectedResource.getHTTP_URI(); log_.log(LogLevel.FINE, "Using redirect cache: " + resource + " -> " + redirectedResource); } redirectLoopPreventionSet.add(url); try { return callback.call(url); } catch (RedirectException re) { log_.log(LogLevel.INFO, re.getMessage()); try { url = new URL(re.getLocation()); String resourceHostApp = resource.getHostApp(); resource = new XMA_URI(url); String newHostApp = resource.getHostApp(); if (!resourceHostApp.equals(newHostApp)) { redirectCache.put(resourceHostApp, newHostApp); // be aware of multiple redirects redirectCache.put(initialResourceHostApp, newHostApp); log_.log(LogLevel.FINE, "Adding redirect cache: " + resourceHostApp + " -> " + newHostApp); } } catch (MalformedURLException e) { throw new ServerException("Illegal HTTP redirect location: " + re.getLocation(), re, re.getReturnCode()); } } } while (!redirectLoopPreventionSet.contains(url) && redirectLoopPreventionSet.size() < 5); throw new ServerException("HTTP redirect loop detected at " + url); } public Result getResourceImpl(final IRtXMASessionClient session, XMA_URI resource, final long modifiedSince, final String etag) throws CommunicationException { RedirectCallback callback = new RedirectCallback() { public Object call(URL url) throws CommunicationException { return getResourceImpl(session, url, modifiedSince, etag); } }; return (Result) callRedirectAware(resource, callback); } private Result getResourceImpl(IRtXMASessionClient session, URL url, long modifiedSince, String etag) throws CommunicationException { /* locals ---------------------------------- */ Result result = new Result(); int code = 0; HttpURLConnection conn; /* locals ---------------------------------- */ try { conn = (HttpURLConnection) url.openConnection(); if(conn instanceof HttpsURLConnection) { ((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier); } sendCookies(session,url,conn); if( etag != null ){ conn.setRequestProperty("If-None-Match", etag); //$NON-NLS-1$ } String strUrl = url.toExternalForm(); if( url.getQuery()==null && ( strUrl.endsWith( ".jar") || strUrl.endsWith( ".xml")) ) { conn.setRequestProperty(Statics.HTTP_CACHE_CONTROL, Statics.HTTP_MAX_AGE + "=0"); //$NON-NLS-1$ } if(modifiedSince>0) { // see sun bugid: 4397096 // if HTTP_Util library is used, the original method may also be used. // conn.setIfModifiedSince(modifiedSince); conn.setRequestProperty( Statics.strIfModifiedSince, HTTPTransport.httpDate(modifiedSince)); } conn.setRequestProperty( Statics.HTTP_ACCEPT,"*/*"); //$NON-NLS-1$ conn.setRequestProperty( Statics.HTTP_USER_AGENT, Statics.HTTP_USER_AGENT_NAME); } catch (IOException exc) { log_.log(LogLevel.WARNING, "error loading '"+url.toString()+"' form server:", exc); //$NON-NLS-1$ throw new ConnectException("error loading '"+url.toString()+"' form server:",exc); } try { code = conn.getResponseCode(); if(code==HttpURLConnection.HTTP_NOT_MODIFIED) { result.contentLength_ = 0; result.lastModified_ = conn.getLastModified(); if(result.lastModified_<=0) { result.lastModified_ = modifiedSince; } result.expirationDate_ = conn.getExpiration(); result.etag_ = conn.getHeaderField( Statics.strEtag ); if(result.etag_==null) { result.etag_ = etag; } log_.log(LogLevel.FINE, "resource not modified: {0}", url.toExternalForm()); //$NON-NLS-1$ } else if(code==HttpURLConnection.HTTP_OK){ result.contentLength_ = conn.getContentLength(); result.lastModified_ = conn.getLastModified(); result.expirationDate_ = conn.getExpiration(); result.etag_ = conn.getHeaderField( Statics.strEtag ); result.transformations_= conn.getHeaderField(Statics.TRANSFORM_HEADER); result.setBuffer( this.readOutput(conn) ); if(result.contentLength_<0) { result.contentLength_=result.buffer_.length; } } else if (code==HttpURLConnection.HTTP_MOVED_TEMP || code==HttpURLConnection.HTTP_MOVED_PERM) { String location = conn.getHeaderField(Statics.HTTP_LOCATION); throw new RedirectException("redirect received from " + url.toString() + " to " + location, code, location); } else { if(code<500) throw new ConnectException("error loading '"+url.toString()+"' from the server:",code); else throw new ServerException("error loading '"+url.toString()+"' from the server:",code); } readCookies(session,url,conn); } catch (RedirectException re) { throw re; } catch (CommunicationException ce) { if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$ log_.log(LogLevel.WARNING, "error loading '"+url.toString()+"' from the server:", ce); //$NON-NLS-1$ throw ce; } catch (Exception ex) { if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$ log_.log(LogLevel.WARNING,"error loading '"+url.toString()+"' from the server:", ex); //$NON-NLS-1$ if(code<500) throw new ConnectException("error loading '"+url.toString()+"' from the server:",ex); else throw new ServerException("error loading '"+url.toString()+"' from the server:",ex); } return result; } public byte[] callServerEvent(final IRtXMASessionClient session,final XMA_URI eventHandler,final byte[] input) throws CommunicationException { return callServerEvent(session, eventHandler, input, false); } /* (non-Javadoc) * @see at.spardat.xma.transport.Transport#callServerEvent(at.spardat.xma.session.XMASessionClient, at.spardat.xma.transport.XMA_URI, java.io.InputStream) */ public byte[] callServerEvent(final IRtXMASessionClient session,final XMA_URI eventHandler,final byte[] input, final boolean handleRedirect) throws CommunicationException { CCLoader swtCLoader = BootRuntime.getInstance().getAppManager().getSWTClassLoader(); if(swtCLoader!=null) { class Inner implements Runnable { byte[] result; CommunicationException exc; public void run() { try { result = callServerEventImpl(session,eventHandler,input, handleRedirect); } catch(CommunicationException exc) { this.exc=exc; } } } Inner inner = new Inner(); try { // call BusyIndicator.showWhile over reflection from the SWT-Classloader Class busyIndicatorClass = swtCLoader.loadClass("org.eclipse.swt.custom.BusyIndicator"); Class displayClass = swtCLoader.loadClass("org.eclipse.swt.widgets.Display"); Method method = busyIndicatorClass.getMethod("showWhile",new Class[]{displayClass,Runnable.class}); method.invoke(null,new Object[]{null,inner}); } catch (Exception exc) { throw new RuntimeException(exc); } if(inner.exc!=null) throw inner.exc; return inner.result; } else { return callServerEventImpl(session,eventHandler,input, handleRedirect); } } private byte[] callServerEventImpl(final IRtXMASessionClient session, XMA_URI eventHandler, final byte[] input, boolean handleRedirect) throws CommunicationException { if (handleRedirect) { RedirectCallback callback = new RedirectCallback() { public Object call(URL url) throws CommunicationException { return callServerEventImpl(session, url, input, true); } }; return (byte[]) callRedirectAware(eventHandler, callback); } else { return callServerEventImpl(session, eventHandler.getHTTP_URI(), input, false); } } private byte[] callServerEventImpl(IRtXMASessionClient session, URL url, byte[] input, boolean handleRedirect) throws CommunicationException { OutputStream serverIn; int code = 0; HttpURLConnection conn; byte[] buffer = null; try { conn = (HttpURLConnection) url.openConnection(); if(conn instanceof HttpsURLConnection) { ((HttpsURLConnection)conn).setHostnameVerifier(hostnameVerifier); } conn.setDoOutput(true); conn.setRequestMethod("POST"); //$NON-NLS-1$ sendCookies(session,url,conn); conn.setRequestProperty( Statics.HTTP_CONTENT_TYPE,"application/octet-stream"); //$NON-NLS-1$ conn.setRequestProperty( Statics.HTTP_ACCEPT,"application/octet-stream"); //$NON-NLS-1$ conn.setRequestProperty( Statics.HTTP_USER_AGENT , Statics.HTTP_USER_AGENT_NAME ); serverIn = conn.getOutputStream(); } catch (IOException exc) { log_.log(LogLevel.WARNING,"error calling '"+url.toString()+"' at the server:", exc); //$NON-NLS-1$ throw new ConnectException("error calling '"+url.toString()+"' at the server:",exc); } try { serverIn.write(input); serverIn.close(); code = conn.getResponseCode(); // if requested, we allow redirect also on POST requests, therewith violating RFC 2616 section 10.3! if (handleRedirect && code==HttpURLConnection.HTTP_MOVED_TEMP || code==HttpURLConnection.HTTP_MOVED_PERM) { String location = conn.getHeaderField(Statics.HTTP_LOCATION); throw new RedirectException("redirect received from " + url.toString() + " to " + location, code, location); } buffer = this.readOutput(conn); readCookies(session,url,conn); return buffer; } catch (RedirectException re) { throw re; } catch (CommunicationException ce) { if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$ log_.log(LogLevel.WARNING, "error calling '"+url.toString()+"' at the server:", ce); //$NON-NLS-1$ throw ce; } catch (Exception ex) { if(code!=0) log_.log(LogLevel.WARNING,"http returncode: {0}",Integer.toString(code)); //$NON-NLS-1$ log_.log(LogLevel.WARNING,"error calling '"+url.toString()+"' at the server:", ex); //$NON-NLS-1$ if(code<500) throw new ConnectException("error calling '"+url.toString()+"' at the server:",ex); else throw new ServerException("error calling '"+url.toString()+"' at the server:",ex); } } /** * get server output into a buffer * * @param conn connection to read from * @return byte[] server output * @throws ServerException content lenght errro * @throws IOException for read erros */ private byte[] readOutput( HttpURLConnection conn ) throws IOException { InputStream serverOut = null; byte[] buffer = null; try{ serverOut = conn.getInputStream(); int len = conn.getContentLength(); int read = 0; int all = 0; if(len>-1) { buffer = new byte[len]; while(read>-1&&all-1) all+=read; } if(read<0) { throw new ServerException("Server reported contentLength "+len+" but send only "+all+" bytes of data"); } return buffer; } else { // if no content length was send, use default size and grow dynamically List bufferList = null; final int defLen = 1024*8; int lastLength = 0; bufferList = new ArrayList(); for(;read>-1;all+=lastLength) { buffer = new byte[defLen]; for(lastLength=0;read>-1&&lastLength-1) lastLength+=read; } bufferList.add(buffer); } byte [] result = new byte[all]; for(int i=0;i0) { cookies = cookieSpec.match(url.getHost(),getPort(url),url.getPath(),"https".equals(url.getProtocol()),cookies); } return cookies; } /** * Takes the cookies from the httpState which match the given url and creates the * corresponsing cookie-header on the given connection. */ private void sendCookies(IRtXMASessionClient session, URL url,HttpURLConnection conn) { Cookie[] cookies = getCookies(url,session); if(cookies!=null&&cookies.length>0) { String cookieHeader = cookieSpec.formatCookies(cookies); conn.setRequestProperty(Statics.HTTP_COOKIE,cookieHeader); if(session!=null && session.getId()==null) { // server side http session was established before client side session was created for(int j=0;j





© 2015 - 2024 Weber Informatics LLC | Privacy Policy