diff --git a/railo-java/libs/apache-commons-httpclient.jar b/railo-java/libs/apache-commons-httpclient.jar index e379af35e3..684ec820fd 100644 Binary files a/railo-java/libs/apache-commons-httpclient.jar and b/railo-java/libs/apache-commons-httpclient.jar differ diff --git a/railo-java/libs/apache-commons-httpcore.jar b/railo-java/libs/apache-commons-httpcore.jar index a64cd2f5e3..498144c447 100644 Binary files a/railo-java/libs/apache-commons-httpcore.jar and b/railo-java/libs/apache-commons-httpcore.jar differ diff --git a/railo-java/libs/apache-commons-httpmime.jar b/railo-java/libs/apache-commons-httpmime.jar index c91c219eb9..788da36a02 100644 Binary files a/railo-java/libs/apache-commons-httpmime.jar and b/railo-java/libs/apache-commons-httpmime.jar differ diff --git a/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPEngine4Impl.java b/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPEngine4Impl.java index 7ff676e8f0..6e7d297aaa 100644 --- a/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPEngine4Impl.java +++ b/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPEngine4Impl.java @@ -4,10 +4,12 @@ import java.io.InputStream; import java.net.URL; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import org.apache.http.Header; import org.apache.http.HttpEntity; @@ -29,11 +31,13 @@ import org.apache.http.client.params.ClientPNames; import org.apache.http.client.protocol.ClientContext; import org.apache.http.conn.params.ConnRoutePNames; +import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.conn.PoolingClientConnectionManager; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.BasicHttpParams; @@ -65,6 +69,23 @@ public class HTTPEngine4Impl { + + private static PoolingClientConnectionManager cm; + private static final int CONNECTION_TTL = 30; + private static Map clients; + public static final boolean CONNECTION_POOLING_ENABLED = true; + private static ConnectionCleaner cc; + static { + clients = new HashMap(); + SchemeRegistry sr = new PoolingClientConnectionManager( ).getSchemeRegistry(); + cm = new PoolingClientConnectionManager( + sr, CONNECTION_TTL, + TimeUnit.SECONDS ); + cm.setDefaultMaxPerRoute(400); + cm.setMaxTotal(1000); + cc = new ConnectionCleaner( cm, CONNECTION_TTL ); + } + /** * does a http get request * @param url @@ -214,18 +235,17 @@ private static HTTPResponse _invoke(URL url,HttpUriRequest request,String userna ProxyData proxy, railo.commons.net.http.Header[] headers, Map formfields) throws IOException { // TODO HttpConnectionManager manager=new SimpleHttpConnectionManager();//MultiThreadedHttpConnectionManager(); - BasicHttpParams params = new BasicHttpParams(); - DefaultHttpClient client = createClient(params,maxRedirect); + DefaultHttpClient client = createClient( maxRedirect, timeout ); HttpHost hh=new HttpHost(url.getHost(),url.getPort()); setHeader(request,headers); if(CollectionUtil.isEmpty(formfields))setContentType(request,charset); setFormFields(request,formfields,charset); setUserAgent(request,useragent); - setTimeout(params,timeout); HttpContext context=setCredentials(client,hh, username, password,false); setProxy(client,request,proxy); if(context==null)context = new BasicHttpContext(); - return new HTTPResponse4Impl(url,context,request,client.execute(request,context)); + HTTPResponse result = new HTTPResponse4Impl(url,context,request,client.execute(request,context)); + return result; } private static void setFormFields(HttpUriRequest request, Map formfields, String charset) throws IOException { @@ -245,11 +265,32 @@ private static void setFormFields(HttpUriRequest request, Map fo } } - public static DefaultHttpClient createClient(BasicHttpParams params, int maxRedirect) { - params.setParameter(ClientPNames.HANDLE_REDIRECTS, maxRedirect==0?Boolean.FALSE:Boolean.TRUE); + public static DefaultHttpClient createClient( int maxRedirect, long timeout ) { + String key = Integer.toString( maxRedirect ) + ":" + Long.toString( timeout ); + if( !clients.containsKey( key )) + clients.put( key, _createClient( maxRedirect, timeout ) ); + return clients.get( key ); + + } + + private static DefaultHttpClient _createClient( int maxRedirect, long timeout ) { + BasicHttpParams params = new BasicHttpParams(); + if( timeout > 0L ) setTimeout(params, timeout); + params.setParameter(ClientPNames.HANDLE_REDIRECTS, maxRedirect==0?Boolean.FALSE:Boolean.TRUE); if(maxRedirect>0)params.setParameter(ClientPNames.MAX_REDIRECTS, new Integer(maxRedirect)); params.setParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, Boolean.FALSE); - return new DefaultHttpClient(params); + + if ( CONNECTION_POOLING_ENABLED ) { + _startConnectionCleanerIfNeeded(); + return new DefaultHttpClient( cm, params ); + } + return new DefaultHttpClient( params ); + } + + private synchronized static void _startConnectionCleanerIfNeeded() { + if ( !cc.isAlive() ) { + cc.start(); + } } private static void setUserAgent(HttpMessage hm, String useragent) { @@ -382,6 +423,30 @@ public static Entity getTemporaryStreamEntity(TemporaryStream ts,String contentT public static Entity getResourceEntity(Resource res, String contentType) { return new ResourceHttpEntity(res,contentType); } - + + private static class ConnectionCleaner extends Thread { + + PoolingClientConnectionManager cm; + int timeout; + ConnectionCleaner ( PoolingClientConnectionManager cm, int timeout ) { + this.cm = cm; + this.timeout = timeout; + setDaemon(true); + } + + public synchronized void run () { + + while(true) { + cm.closeExpiredConnections(); + cm.closeIdleConnections( timeout, TimeUnit.SECONDS); + try { + Thread.sleep( 5000 ); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } } diff --git a/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPResponse4Impl.java b/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPResponse4Impl.java index d027f01768..71b9514d41 100644 --- a/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPResponse4Impl.java +++ b/railo-java/railo-core/src/railo/commons/net/http/httpclient4/HTTPResponse4Impl.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -23,13 +25,30 @@ public class HTTPResponse4Impl extends HTTPResponseSupport implements HTTPRespon HttpResponse rsp; HttpUriRequest req; private URL url; - private HttpContext context; + private HttpContext context; + private PipedInputStream is = null; public HTTPResponse4Impl(URL url,HttpContext context, HttpUriRequest req,HttpResponse rsp) { this.url=url; this.context=context; this.req=req; this.rsp=rsp; + if( HTTPEngine4Impl.CONNECTION_POOLING_ENABLED ) { + // create thread to push content to a surrogate input stream, so that this class can appropriately close the underlying content inputstream and + // release connections back to the connection pool + HttpEntity e = rsp.getEntity(); + if(e != null) { + is = new PipedInputStream(); + PipedOutputStream os = null; + try { + os = new PipedOutputStream( is ); + new Thread( new HTTPContentPiper( e.getContent(), os ) ).start(); + } + catch (IOException e1) { + // is will be null + } + } + } } @Override @@ -40,11 +59,10 @@ public String getContentAsString() throws IOException { @Override public String getContentAsString(String charset) throws IOException { - HttpEntity entity = rsp.getEntity(); InputStream is=null; if(StringUtil.isEmpty(charset,true))charset=getCharset(); try{ - return IOUtil.toString(is=entity.getContent(), charset); + return IOUtil.toString(is=getContentAsStream(), charset); } finally { IOUtil.closeEL(is); @@ -55,7 +73,11 @@ public String getContentAsString(String charset) throws IOException { public InputStream getContentAsStream() throws IOException { HttpEntity e = rsp.getEntity(); if(e==null) return null; + if( HTTPEngine4Impl.CONNECTION_POOLING_ENABLED ) { + return is; + } return e.getContent(); + } @Override @@ -64,7 +86,7 @@ public byte[] getContentAsByteArray() throws IOException { InputStream is=null; if(entity==null) return new byte[0]; try{ - return IOUtil.toBytes(is=entity.getContent()); + return IOUtil.toBytes(is=getContentAsStream()); } finally { IOUtil.closeEL(is); @@ -157,4 +179,31 @@ public Header[] getAllHeaders() { } return trg; } + + /** + * consumes an http inputstream and closes it + */ + private class HTTPContentPiper implements Runnable { + + private InputStream is; + private PipedOutputStream os; + + public HTTPContentPiper( InputStream is, PipedOutputStream os ) { + this.os = os; + this.is = is; + } + @Override + public void run() { + try { + IOUtil.copy( is, os,false,false ); + } + catch (IOException e) { + e.printStackTrace(); + } + + IOUtil.closeEL( is ); + IOUtil.closeEL( os ); + } + + } } diff --git a/railo-java/railo-core/src/railo/runtime/tag/Http4.java b/railo-java/railo-core/src/railo/runtime/tag/Http4.java index 9406f2ad96..953f37986a 100755 --- a/railo-java/railo-core/src/railo/runtime/tag/Http4.java +++ b/railo-java/railo-core/src/railo/runtime/tag/Http4.java @@ -603,8 +603,7 @@ public int doEndTag() throws PageException { private void _doEndTag(Struct cfhttp) throws PageException, IOException { - BasicHttpParams params = new BasicHttpParams(); - DefaultHttpClient client = HTTPEngine4Impl.createClient(params,redirect?HTTPEngine.MAX_REDIRECT:0); + DefaultHttpClient client = HTTPEngine4Impl.createClient( redirect?HTTPEngine.MAX_REDIRECT:0, (int)this.timeout ); ConfigWeb cw = pageContext.getConfig(); @@ -902,9 +901,6 @@ else if(type.equals("body")) { if(!hasHeaderIgnoreCase(req,"User-Agent")) req.setHeader("User-Agent",this.useragent); - // set timeout - if(this.timeout>0L)HTTPEngine4Impl.setTimeout(params, (int)this.timeout); - // set Username and Password if(this.username!=null) { if(this.password==null)this.password="";