第八章
8.高级主题
8.1.自定义客户端连接
在某些情况下,可能有必要通过线路发送HTTP消息浏览超出什么是可能的,以便能够处理非标准,不符合规定的行为使用HTTP参数的方式来定制。例如,对于Web爬虫,它可能是需要强制的HttpClient接受格式错误的响应头,以挽救内容的消息。
通常情况下,插入一个自定义的的消息解析器或一个自定义的连接实现的过程包括几个步骤:
提供自定义LineParser / LineFormatter接口的实现。实现消息的解析/格式化逻辑的要求。
class MyLineParser extends BasicLineParser { @Override public Header parseHeader( final CharArrayBuffer buffer) throws ParseException { try { return super.parseHeader(buffer); } catch (ParseException ex) { // Suppress ParseException exception return new BasicHeader("invalid", buffer.toString()); } } }
请提供一个自定义OperatedClientConnection实现。更换默认的请求/响应解析器,请求/响应格式化用自定义的需要。实现读/写代码,如果需要不同的信息。
class MyClientConnection extends DefaultClientConnection { @Override protected HttpMessageParser createResponseParser( final SessionInputBuffer buffer, final HttpResponseFactory responseFactory, final HttpParams params) { return new DefaultResponseParser( buffer, new MyLineParser(), responseFactory, params); } }
提供一个自定义ClientConnectionOperator接口实现,以创建新类的连接。必要时,实现不同的套接字初始化代码。
class MyClientConnectionOperator extends DefaultClientConnectionOperator { public MyClientConnectionOperator(final SchemeRegistry sr) { super(sr); } @Override public OperatedClientConnection createConnection() { return new MyClientConnection(); } }
以创建新类的连接运营商提供一个自定义的ClientConnectionManager接口实现。
class MyClientConnManager extends SingleClientConnManager { public MyClientConnManager( final HttpParams params, final SchemeRegistry sr) { super(params, sr); } @Override protected ClientConnectionOperator createConnectionOperator( final SchemeRegistry sr) { return new MyClientConnectionOperator(sr); } }
8.2.有状态的HTTP连接
尽管HTTP规范假定会话状态信息嵌入在HTTP消息中的HTTP cookies的形式,因此HTTP连接总是无状态的,这个假设并不总是成立的现实生活中。有情况下与特定的用户身份或在一个特定的安全上下文创建HTTP连接时,因此,不能与其他用户共享,并且可以重复使用只由同一用户。这种状态的HTTP连接的例子NTLM身份验证的连接和SSL连接的客户端证书身份验证。
8.2.1.用户令牌处理程序
HttpClient的依赖于UserTokenHandler接口,以确定是否在给定的执行上下文是用户特定的,或并不。由该处理程序返回的唯一标识当前用户如果上下文是用户特定的背景下为空,如果不包含任何资源或具体到当前用户的令牌对象。将被使用的用户令牌,以确保用户特定的资源不会被共享或其他用户重复使用。
默认实现的UserTokenHandler接口使用的主要类的一个实例代表一个状态对象为HTTP连接,如果它可以从指定的执行上下文。 DefaultUserTokenHandler将使用基于连接的认证计划,如NTLM或打开客户端身份验证的SSL会话的用户的原则。如果两者都无法使用,将返回null标记。
用户可以提供自定义默认的执行情况,如果不符合他们的需求:
DefaultHttpClient httpclient = new DefaultHttpClient(); httpclient.setUserTokenHandler(new UserTokenHandler() { public Object getUserToken(HttpContext context) { return context.getAttribute("my-token"); } });
8.2.2.用户令牌和执行上下文
在HTTP请求执行的过程中,HttpClient的将下面的用户身份相关的对象到执行上下文:
http.user ClientContext.USER_TOKEN='令牌':对象实例代表实际的用户身份,一般预期的原理接口的一个实例
人们可以发现,或不使用的连接是否执行的要求是有状态的请求被执行后,检查本地HTTP上下文的内容。
DefaultHttpClient httpclient = new DefaultHttpClient(); HttpContext localContext = new BasicHttpContext(); HttpGet httpget = new HttpGet("http://localhost:8080/"); HttpResponse response = httpclient.execute(httpget, localContext); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); Object userToken = localContext.getAttribute(ClientContext.USER_TOKEN); System.out.println(userToken);
8.2.2.1.持久性状态的连接
请注意,只有在相同的状态对象被绑定到执行上下文中执行请求时,可以重复使用一个持久连接,带有一个状态对象。因此,它是非常重要的,以确保无论是重复使用相同的情况下执行随后的HTTP请求由同一个用户或用户令牌绑定之前请求执行的背景下。
DefaultHttpClient httpclient = new DefaultHttpClient(); HttpContext localContext1 = new BasicHttpContext(); HttpGet httpget1 = new HttpGet("http://localhost:8080/"); HttpResponse response1 = httpclient.execute(httpget1, localContext1); HttpEntity entity1 = response1.getEntity(); EntityUtils.consume(entity1); Principal principal = (Principal) localContext1.getAttribute( ClientContext.USER_TOKEN); HttpContext localContext2 = new BasicHttpContext(); localContext2.setAttribute(ClientContext.USER_TOKEN, principal); HttpGet httpget2 = new HttpGet("http://localhost:8080/"); HttpResponse response2 = httpclient.execute(httpget2, localContext2); HttpEntity entity2 = response2.getEntity(); EntityUtils.consume(entity2);