第二章
2.连接管理
HttpClient假设连接的初始化和终止的过程中,以及活动连接的I / O操作的完全控制权。然而,连接操作的各个方面的可以影响使用的参数数量。
2.1.连接参数
这些参数可以影响连接操作:
CoreConnectionPNames.SO_TIMEOUT
='http.socket.timeout': 定义的插座的超时(SO_TIMEOUT),以毫秒为单位,这是超时等待数据或者,换句话说,两个连续的数据包之间的最大闲置时间)。超时值为0被解释为无穷大超时。这个参数期望得到一个java.lang.Integer类型的值。如果该参数没有设置,读操作不会超时(无限超时时间)。
CoreConnectionPNames.TCP_NODELAY
='http.tcp.nodelay': 决定是否Nagle算法是要使用。 Nagle算法试图最大限度地减少发送的段数,以节省带宽。当应用程序希望降低网络延迟,提高性能,他们可以禁用Nagle算法(也就是使TCP_NODELAY。数据将被发送,在成本增加的带宽消耗。这个参数期望得到一个java.lang.Boolean类型的值如果该参数没有设置,TCP_NODELAY将被启用(无延迟)。
CoreConnectionPNames.SOCKET_BUFFER_SIZE
='http.socket.buffer-size':确定用于缓冲数据,同时接收/发送HTTP消息的内部套接字缓冲区的大小。这个参数期望得到一个java.lang.Integer类型的值。如果该参数没有设置,HttpClient会分配8192字节的socket缓冲区。
CoreConnectionPNames.SO_LINGER
='http.socket.linger':设置SO_LINGER指定的延迟时间,以秒为单位。最大超时值是特定于平台的。值0意味着该选项被禁用。的JRE,则使用默认值-1表示。该设置仅影响的套接字关闭操作。如果该参数没有设置,值-1(JRE默认)将假定。
CoreConnectionPNames.CONNECTION_TIMEOUT
='http.connection.timeout':决定以毫秒为单位的超时时间,直到建立连接。超时值为0被解释为无穷大超时。这个参数期望得到一个java.lang.Integer类型的值。如果这个参数没有被设置,连接操作不会超时(无限超时时间)。
CoreConnectionPNames.STALE_CONNECTION_CHECK
='http.connection.stalecheck':判断是否是要使用陈旧的连接检查。禁用陈旧的连接检查,可能会导致显着的性能改进(检查可以造成高达30毫秒,每个请求的开销),在已关闭的连接在服务器端执行请求时,I / O错误的风险。这个参数期望得到一个java.lang.Boolean类型的值。对于性能关键的操作检查应该被禁用。如果该参数没有设置,陈旧的连接检查每个请求执行之前执行。
CoreConnectionPNames.MAX_LINE_LENGTH
='http.connection.max-line-length': 确定的最大行长度的限制。如果设置为正值,任何HTTP行超过此限制将导致java.io.IOException异常。为负或零值将有效地禁用检查。这个参数期望得到一个java.lang.Integer类型的值。如果该参数没有设置,没有限制将被强制执行。
CoreConnectionPNames.MAX_HEADER_COUNT
='http.connection.max-header-count': 决定允许的最大HTTP标头计数。如果设置为正值,接收到的数据流超过此限制的HTTP标头将导致java.io.IOException异常。为负或零值将有效地禁用检查。这个参数期望得到一个java.lang.Integer类型的值。如果该参数没有设置,没有限制将被强制执行。
ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE
='http.connection.max-status-line-garbage':定义的最大数量可忽略的行之前,我们期待一个HTTP响应的状态行。 HTTP/1.1持久连接,问题就出现了,破碎的脚本可以返回一个错误的内容长度(有更多的字节发送比指定)。不幸的是,在某些情况下,这不能被检测到后的不良反应,但仅之前下一个。所以HttpClient的必须这样可以跳过那些多余的线。这个参数期望得到一个java.lang.Integer类型的值。 0禁止所有垃圾/空行状态行前。使用无限数量为java.lang.Integer#MAX_VALUE。如果该参数没有设置,将承担无限数量。
2.2.持久性连接
从一台主机到另一台建立连接的过程是相当复杂的,涉及多个数据包在两个端点之间的交流,它可以是相当费时。连接握手的开销,可以显着的,尤其是对小的HTTP消息。一个可以实现非常高的数据吞吐量,如果打开的连接可以被重新使用,以执行多个请求。
HTTP/1.1 HTTP连接可以用于多个请求数默认情况下。 HTTP/1.0兼容的终端也可以使用一种机制,明确地传达自己的喜好来保持连接处于活动状态,并使用它的多个请求。 HTTP代理也可以保持空闲连接的存活了一段时间的情况下,需要连接到相同的目标主机的后续请求。保持连接活动的能力通常为持久性连接。 HttpClient的完全支持持久性连接。
2.3. HTTP连接路由
HttpClient的是能够建立连接到目标主机可直接或通过一个途径,可能涉及多个中间连接 - 也简称为跳。 HttpClient的区分平原,隧道和分层路由的连接。使用隧道连接到目标主机的多个中间代理被称为“代理链”。
平原建立路由连接到目标或第一个也是唯一的代理。隧道路由的建立,通过连接到第一和隧穿到目标代理链。不使用代理服务器的路由不能隧道。分层路由建立的分层协议在现有的连接。在隧道的目标,或通过直接连接,无需代理协议只能进行分层。
2.3.1.路由计算
RouteInfo接口表示一个明确的路由信息到目标主机的一个或多个中间步骤或跳。 HttpRoute是一个具体的实施RouteInfo,不能改变的是不可改变的。 HttpTracker是一个可变RouteInfo的实现,由HttpClient在内部使用的最终路由目标跟踪的剩余跳数。 HttpTracker可更新的下一跳成功执行后,向路由的目标。 HttpRouteDirector是一个辅助类,可用于计算路由中的下一个步骤。由HttpClient在内部使用这个类。
HttpRoutePlanner是一个接口,代表一个完整的路由策略来计算给定的目标的基础上执行上下文。 HttpClient附带有两个的默认HttpRoutePlanner实现。是根据java.net.ProxySelector ProxySelectorRoutePlanner。默认情况下,它会选择的代理服务器设置的JVM,无论是从系统属性或从浏览器中运行的应用程序。 DefaultHttpRoutePlanner实现不使用任何Java系统属性,也没有任何系统或浏览器的代理设置。计算完全基于下述参数的HTTP路由。
2.3.2.安全的HTTP连接
HTTP连接可以被认为是安全的,如果两个连接端点之间传输的信息不能被读取或篡改,未经授权的第三方。 SSL / TLS协议是使用最广泛的技术,以确保HTTP传输的安全性。然而,其他的加密技术,可以为好。通常情况下,HTTP传输之上的SSL / TLS加密连接。
2.4. HTTP路由参数
这些参数可以影响路由计算:
ConnRoutePNames.DEFAULT_PROXY
='http.route.default-proxy':定义默认情况下的的路线规划者,不要让使用JRE设置代理主机使用。这个参数期望得到一个HttpHost类型的值。如果该参数没有设置,将尝试直接连接到目标。
ConnRoutePNames.LOCAL_ADDRESS
='http.route.local-address': 定义一个本地地址要使用的所有默认路由规划者。在具有多个网络接口的机器上,此参数可用于选择网络接口连接。这个参数期望得到一个java.net.InetAddress类型的值。如果该参数没有设置,默认的本地地址会被自动使用。
ConnRoutePNames.FORCED_ROUTE
='http.route.forced-route':定义一个强制路由使用的所有默认路由规划者。计算路由,而不是强制路由将被退回,即使它指向一个完全不同的目标主机。此参数预计一个值类型HttpRoute。
2.5.套接字工厂
HTTP连接使用java.net.Socket对象内部处理的数据通过网线传输。不过,他们依靠,上的SchemeSocketFactory接口来创建,初始化和连接插座。这使得HttpClient的用户提供特定于应用程序的套接字初始化代码在运行时。 PlainSocketFactory是出厂默认为创建和初始化纯的(未加密)插座。
创建套接字,并将其连接到一个主机的方法,解耦,而被阻塞的连接操作中,可以被关闭,以便在插槽。
用法如下:
PlainSocketFactory sf = PlainSocketFactory.getSocketFactory(); Socket socket = sf.createSocket(); HttpParams params = new BasicHttpParams(); params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L); InetSocketAddress address = new InetSocketAddress("locahost", 8080); sf.connectSocket(socket, address, null, params);
2.5.1.安全套接字分层
SchemeLayeredSocketFactory是的延伸的SchemeSocketFactory接口。分层的套接字工厂可以创建套接字之上现有的普通插座。插座分层主要用于创建安全套接字通过代理。 HttpClient附带了SSLSocketFactory的,实施SSL / TLS分层的。请注意HttpClient不使用任何自定义的加密功能。它完全依赖于标准的Java加密法(JCE)和安全套接字(JSEE)扩展。
2.5.2. SSL / TLS定制
HttpClient的使用SSLSocketFactory的创建SSL连接。 SSLSocketFactory的可高度定制。它可以采取一个的javax.net.ssl.SSLContext实例作为参数,并用它来创建自定义配置的SSL连接。
用法如下:
HttpParams params = new BasicHttpParams(); SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(null, null, null); SSLSocketFactory sf = new SSLSocketFactory(sslcontext); SSLSocket socket = (SSLSocket) sf.createSocket(params); socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" }); params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L); InetSocketAddress address = new InetSocketAddress("locahost", 443); sf.connectSocket(socket, address, null, params);
自定义SSLSocketFactory的SSL / TLS协议,详细的解释,这是本文档的范围的概念隐含着一定的熟悉程度。javax.net.ssl.SSLContext及相关工具的详细说明,请参阅Java安全套接字扩展。
2.5.3.主机名验证
除了信任验证和在SSL / TLS协议级上执行的客户端认证,HttpClient可以任选验证目标主机名是否匹配存储的名称服务器的X.509证书,一旦已建立连接内部。这种验证可以提供额外担保的服务器信任材料的真实性。个X509主机名验证器接口的主机名验证的策略。 HttpClient附带了3个X509主机名验证器实现。注意:主机名验证不应该混淆与SSL信任验证。
StrictHostnameVerifier:严格主机名验证的Sun Java6的Sun Java1.4,Java 5中,以同样的方式。这也是非常接近IE6。这种实现似乎是符合RFC 2818的处理通配符。主机名必须匹配第一个CN,或任何主题的小号。可能会出现通配符的CN,并在任何主题的小号。
BrowserCompatHostnameVerifier:主机名验证器的工作方式相同,卷曲和Firefox。主机名必须匹配第一个CN,或任何主题的小号。可能会出现通配符的CN,并在任何主题的小号。唯一的区别之间BrowserCompatHostnameVerifier和StrictHostnameVerifier是一个通配符(如“*。foo.com”)BrowserCompatHostnameVerifier匹配所有的子域,包括“abfoo.com”。
AllowAllHostnameVerifier:此主机名验证器基本上是关闭主机名验证关闭。这个实现是一个no-op,,不会抛出javax.net.ssl.SSLException。
每默认HttpClient的使用BrowserCompatHostnameVerifier的实施。如果需要的话,你可以指定一个不同的主机名验证器实现
用法如下:
SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(null, null, null); SSLSocketFactory sf = new SSLSocketFactory( sslcontext, SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
2.6.协议计划
该计划类代表一个协议方案,如“http”或“https”开头,并包含了一些协议的属性,如默认的端口和套接字工厂使用到创建java.net.Socket的情况下,给定的协议。 SchemeRegistry类是用来维护一组的计划,,HttpClient可以选择从当试图建立连接的请求URI:
用法如下:
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory()); SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(null, null, null); SSLSocketFactory sf = new SSLSocketFactory( sslcontext, SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); Scheme https = new Scheme("https", 443, sf); SchemeRegistry sr = new SchemeRegistry(); sr.register(http); sr.register(https);
2.7. HttpClient的代理配置
即使是HttpClient知道的复杂的路由scemes,代理链,它仅支持简单的直接或单跳的开箱即用的代理服务器连接。
告诉HttpClient的通过代理服务器连接到目标主机的最简单的方法是通过设置默认的代理参数:
DefaultHttpClient httpclient = new DefaultHttpClient(); HttpHost proxy = new HttpHost("someproxy", 8080); httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
还可以指示HttpClient的使用标准JRE代理选择器获取代理信息:
DefaultHttpClient httpclient = new DefaultHttpClient(); ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner( httpclient.getConnectionManager().getSchemeRegistry(), ProxySelector.getDefault()); httpclient.setRoutePlanner(routePlanner);
另外,我们可以提供一个自定义RoutePlanner实现,才能有一个完整的控制权的过程中,HTTP路由计算:
DefaultHttpClient httpclient = new DefaultHttpClient(); httpclient.setRoutePlanner(new HttpRoutePlanner() { public HttpRoute determineRoute( HttpHost target, HttpRequest request, HttpContext context) throws HttpException { return new HttpRoute(target, null, new HttpHost("someproxy", 8080), "https".equalsIgnoreCase(target.getSchemeName())); } });
2.8. HTTP连接管理器
2.8.1.连接运营商
操作连接的底层套接字或国家可以操纵外部实体,通常被称为作为连接运营商的客户端连接。该OperatedClientConnection接口扩展HttpClientConnection的接口,定义了额外的方法来管理连接插座。 ClientConnectionOperator接口的策略创建OperatedClientConnection实例和更新这些对象的底层套接字。实现将最有可能使使用一个SchemeSocketFactory的创建java.net.Socket的情况下。 ClientConnectionOperator接口使HttpClient的用户提供了一个用于连接运营商的定制策略,以及能够提供的另一种实现的OperatedClientConnection接口。
2.8.2.管理连接和连接管理器
HTTP连接是复杂的,有状态的线程安全的对象,需要妥善管理才能正常工作。 HTTP的连接只能使用一个执行线程的时间。 HttpClient的采用了一种特殊的实体来管理访问HTTP连接HTTP连接管理器所代表的ClientConnectionManager接口。 HTTP连接管理器的目的是,作为一个新的HTTP连接的工厂,管理持久连接和同步访问持久性连接,确保一次只有一个线程可以访问一个连接。
内部HTTP连接管理器与实例OperatedClientConnection,但他们返回的实例ManagedClientConnection的向服务消费者。管理其状态,并控制所有的I / O操作,连接的一个OperatedClientConnection实例的包装ManagedClientConnection行为。它也抽象了套接字操作,并提供方便的方法来打开和更新,以建立路由的插座。 ManagedClientConnection实例都知道他们的链接到产生它们的连接管理器的事实,他们必须返回到管理器时,不再使用。 ManagedClientConnection类也实现了ConnectionReleaseTrigger接口,可以用来触发释放连接返回给经理。连接释放后已经引发了包裹连接被分离从ManagedClientConnection包装,和的OperatedClientConnection的实例返回到经理。即使服务消费者仍然持有一个的参考的ManagedClientConnection例如,它不再能够执行任何I / O操作,或有意或无意地改变状态的OperatedClientConnection。
这是从连接管理器获取连接的一个例子:
Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory()); SchemeRegistry sr = new SchemeRegistry(); sr.register(http); ClientConnectionManager connMrg = new BasicClientConnectionManager(sr); // Request new connection. This can be a long process ClientConnectionRequest connRequest = connMrg.requestConnection( new HttpRoute(new HttpHost("localhost", 80)), null); // Wait for connection up to 10 sec ManagedClientConnection conn = connRequest.getConnection(10, TimeUnit.SECONDS); try { // Do useful things with the connection. // Release it when done. conn.releaseConnection(); } catch (IOException ex) { // Abort connection upon an I/O error. conn.abortConnection(); throw ex; }
连接请求,可以提前终止必要时调用ClientConnectionRequest:#abortRequest()。这将解除封锁线程阻塞在ClientConnectionRequest#的getConnection()方法。
一旦响应内容已被完全消耗,BasicManagedEntity包装类,可以用来确保自动释放底层连接。 HttpClient的内部使用此机制实现透明连接释放所有反应从HttpClient#执行()方法:
ClientConnectionRequest connRequest = connMrg.requestConnection( new HttpRoute(new HttpHost("localhost", 80)), null); ManagedClientConnection conn = connRequest.getConnection(10, TimeUnit.SECONDS); try { BasicHttpRequest request = new BasicHttpRequest("GET", "/"); conn.sendRequestHeader(request); HttpResponse response = conn.receiveResponseHeader(); conn.receiveResponseEntity(response); HttpEntity entity = response.getEntity(); if (entity != null) { BasicManagedEntity managedEntity = new BasicManagedEntity(entity, conn, true); // Replace entity response.setEntity(managedEntity); } // Do something useful with the response // The connection will be released automatically // as soon as the response content has been consumed } catch (IOException ex) { // Abort connection upon an I/O error. conn.abortConnection(); throw ex; }
2.8.3.简单的连接管理器
BasicClientConnectionManager是一个简单的连接管理器保持在一个时间只有一个连接。尽管这个类是线程安全的,它应该只使用一个执行线程。 BasicClientConnectionManager将作出努力,以重用连接的后续请求具有相同的路线。 ,但是,它会关闭现有的连接,然后重新打开它给定的路线,如果持续连接的路线不匹配的连接请求。如果连接已经被分配,那么将引发java.lang.IllegalStateException。
默认情况下,HttpClient的每BasicClientConnectionManager使用。
2.8.4.连接池管理器
PoolingClientConnectionManager是一个比较复杂的管理客户端连接池的实现,是可以服务多个执行线程的连接请求。连接池每一个路线的基础上。经理已经有一个持久连接池的路由的请求将租赁从池中的连接,而不是创建一个全新的连接服务。
PoolingClientConnectionManager保持最高限额为每一个路线的基础上,在总的连接。每默认情况下,此实现将创建2个以上的并发连接数给定的路线和总共不超过20个连接。对于许多现实世界的应用程序,这些限制可能过于限制,特别是如果他们使用HTTP作为传输协议为他们服务。连接限制,可以使用适当的HTTP参数进行调整。
这个例子显示了如何连接池的参数可以进行调整:
SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register( new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); schemeRegistry.register( new Scheme("https", 443, SSLSocketFactory.getSocketFactory())); PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry); // Increase max total connection to 200 cm.setMaxTotal(200); // Increase default max connection per route to 20 cm.setDefaultMaxPerRoute(20); // Increase max connections for localhost:80 to 50 HttpHost localhost = new HttpHost("locahost", 80); cm.setMaxPerRoute(new HttpRoute(localhost), 50); HttpClient httpClient = new DefaultHttpClient(cm);
2.8.5.连接管理器关闭
当不再需要一个HttpClient的实例,是走出去的范围,重要的是要关闭的连接管理器,以确保所有的连接保持活着的经理关闭这些连接分配系统资源被释放。
用法如下:
DefaultHttpClient httpclient = new DefaultHttpClient(); HttpGet httpget = new HttpGet("http://www.google.com/"); HttpResponse response = httpclient.execute(httpget); HttpEntity entity = response.getEntity(); System.out.println(response.getStatusLine()); EntityUtils.consume(entity); httpclient.getConnectionManager().shutdown();
2.9.多线程执行请求
当配有一个连接池管理器,如PoolingClientConnectionManager,HttpClient可以被用来执行多个请求同时使用多个线程的执行。
PoolingClientConnectionManager将分配根据其配置的连接。如果所有连接对于一个给定的路线已经被租用,连接请求将被阻塞,直到连接被释放回池中。一个连接管理器可以确保不会无限期地阻塞在连接请求中的操作的设置'http.conn-manager.timeout'为正值。如果不能在给定的时间内ConnectionPoolTimeoutException服务的连接请求将被抛出。
用法如下:
SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register( new Scheme("http", 80, PlainSocketFactory.getSocketFactory())); ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry); HttpClient httpClient = new DefaultHttpClient(cm); // URIs to perform GETs on String[] urisToGet = { "http://www.domain1.com/", "http://www.domain2.com/", "http://www.domain3.com/", "http://www.domain4.com/" }; // create a thread for each URI GetThread[] threads = new GetThread[urisToGet.length]; for (int i = 0; i < threads.length; i++) { HttpGet httpget = new HttpGet(urisToGet[i]); threads[i] = new GetThread(httpClient, httpget); } // start the threads for (int j = 0; j < threads.length; j++) { threads[j].start(); } // join the threads for (int j = 0; j < threads.length; j++) { threads[j].join(); }
虽然HttpClient的实例不仅是线程安全的,可以执行多个线程之间的共享,这是强烈建议每一个线程维护其自己的专用的HttpContext的实例。
用法如下:
static class GetThread extends Thread { private final HttpClient httpClient; private final HttpContext context; private final HttpGet httpget; public GetThread(HttpClient httpClient, HttpGet httpget) { this.httpClient = httpClient; this.context = new BasicHttpContext(); this.httpget = httpget; } @Override public void run() { try { HttpResponse response = this.httpClient.execute(this.httpget, this.context); HttpEntity entity = response.getEntity(); if (entity != null) { // do something useful with the entity } // ensure the connection gets released to the manager EntityUtils.consume(entity); } catch (Exception ex) { this.httpget.abort(); } } }
2.10.连接收回策略
经典的非阻塞I / O模型的主要缺点之一是,网络插座,可挡在I / O操作时,I / O事件作出反应。当一个连接被释放回的经理,它可以一直持续下去,但它是无法监视状态的插座和任何I/ O事件作出反应。如果连接被关闭在服务器端,客户端的连接是无法检测到处于连接状态的变化(并作出适当反应通过关闭上的插座的端部)。
HttpClient的测试连接是否“过时”,即不再有效,因为它被关闭了在服务器端,使用前执行HTTP请求的连接试图缓解这一问题。陈旧的连接检查是不是100%可靠,并增加了10到30毫秒的每个请求执行的开销。唯一可行的解决方案,不涉及一个线程,每个插座型号空闲连接的是一个专用的监视器线程,,驱逐连接由于长时间的闲置,被认为是过期的。监视器的线程可以周期性地调用ClientConnectionManager#closeExpiredConnections()方法来关闭所有过期的连接和驱逐关闭的连接池。它也可以随意调用ClientConnectionManager#closeIdleConnections()方法关闭所有连接在给定的时间内已空闲。
用法如下:
public static class IdleConnectionMonitorThread extends Thread { private final ClientConnectionManager connMgr; private volatile boolean shutdown; public IdleConnectionMonitorThread(ClientConnectionManager connMgr) { super(); this.connMgr = connMgr; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); // Close expired connections connMgr.closeExpiredConnections(); // Optionally, close connections // that have been idle longer than 30 sec connMgr.closeIdleConnections(30, TimeUnit.SECONDS); } } } catch (InterruptedException ex) { // terminate } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } }
2.11.连接保持活动的策略
HTTP规范不指定多长时间的持久连接,并应保持活着。一些HTTP服务器使用的是非标准保持活动的头传达给客户端,以秒为单位的一段时间,他们打算在服务器端保持连接。 HttpClient的使用如果有此信息。如果的Keep-Alive头的响应是不存在的,HttpClient假设的连接可以保持活着下去。然而,许多在一般使用的HTTP服务器在闲置一段时间后,经常不通知客户的情况下,为了节省系统资源,配置为丢弃的持久连接。情况下,默认的策略证明是过于乐观,可能要提供一个自定义的保持活动的策略。
用法如下:
DefaultHttpClient httpclient = new DefaultHttpClient(); httpclient.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() { public long getKeepAliveDuration(HttpResponse response, HttpContext context) { // Honor 'keep-alive' header HeaderElementIterator it = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; } catch(NumberFormatException ignore) { } } } HttpHost target = (HttpHost) context.getAttribute( ExecutionContext.HTTP_TARGET_HOST); if ("www.naughty-server.com".equalsIgnoreCase(target.getHostName())) { // Keep alive for 5 seconds only return 5 * 1000; } else { // otherwise keep alive for 30 seconds return 30 * 1000; } } });