参考资料:

http://www.systinet.com/doc/wasp_uddi/uddi/igpreliminary.html

教程中的一个例程,可以​​下载​​。

来源:​​竹笋炒肉​


虽然用telnet这样的程序都可把页面取回来,但是在与web服务器的交互中,如果涉及cookie或https或ssl等内容,一般功能相对完备的http客户端还是非常必要的。IE或NetScape等浏览器确实不错,可是如果为实现持续互动而在程序调用浏览器,我个人认为其中的工作量还是不小的,这还没考虑版权问题。最好的办法,就是能有一个开源的包,能实现http客户端的功能,供我们开发的程序调用。​​httpclient​​就是这么一个包,我相信可能有比它的实现更好的,但目前我只关注这个。:)

  下面是nogoop做的功能比较表:

Features

nogoop

Sun JRE < 1.4.2

Sun JRE 1.4.2

Innovation

Apache/Jakarta

cookies

X

X

plug compatible

X

X

X

X

[partial]

true request output stream

X

X

true response input stream

X

X

X

connection keep alive

X

X

X

X

X

connection pool throttling

X

X

connection/request timeout

X

X [uns]

X

X

idle connection timeout

X

X

pipelining of requests

X

alternate DNS resolution (dnsjava)

X

SSL

X

X

X

X

X

basic authentication

X

X

X

X

X

digest authentication

X

X

X

X

X

NTLM authentication

X

[Windows only]

X

proxy authentication

X

X

X

X

X

minimum JRE version

1.2

1

01年4月2日

1.2

1.2

price

$499

free

free

free

free

source available

X

X

X

diagnostic tracing

X

X

X

actively supported

X

X

X

X

fix turnaround

fast

slow

slow

none

medium

license

purchase

Sun JRE

Sun JRE

LGPL

Apache


1、HttpClient的功能



  1. 基于标准,纯正java,实现了http1.0和1.1。
  2. 在一个可扩展的OO框架内,实现了HTTP的全部方法(GET, POST,
    PUT, DELETE, HEAD, OPTIONS, and TRACE)
  3. 支持HTTPS(ssl上的HTTP)的加密操作
  4. 透明地穿过HTTP代理建立连接
  5. 通过CONNECT方法,利用通过建立穿过HTTP代理的HTTPS连接
  6. 利用本地Java socket,透明地穿过SOCKS(版本5和4)代理建立连接
  7. 支持利用Basic、Digest和NTLM加密的认证
  8. 支持用于上传大文件的Multi-Part表单POST方法
  9. 插件式安全socket实现,易于使用第三方的解决方案
  10. 连接管理,支持多线程应用,支持设定单个主机总连接和最高连接数量,自动检测和关闭失效连接
  11. 直接将请求信息流送到服务器的端口
  12. 直接读取从服务器的端口送出的应答信息
  13. 支持HTTP/1.0中用KeepAlive和HTTP/1.1中用persistance设置的持久连接
  14. 直接访问由服务器送出的应答代码和头部信息
  15. 可设置连接超时时间

  16. HttpMethods 实现Command Pattern,以允许并行请求或高效连接复用
  17. 遵循the Apache Software License协议,源码免费可得


2、预备工作


对jre1.3.*,如果要HttpClient支持https,则需要下载并安装​​jsse​​​和​​jce​​.安装的步骤如下:

1)下载jsse和jce.

2)检查CLASSPATH中没有与jsse和jce相关的jar包

3)将 US_export_policy.jar、local_policy.jar、jsse.jar、jnet.jar、jce1_2_x.jar、sunjce_provider.jar、jcert.jar复制到目录:

UNIX:$JDK_HOME/jre/lib/ext

Windows:%JDK_HOME%\jre\lib\ext

4)修改下述目录下的java.security文件。

UNIX:$JDK_HOME/jre/lib/security/

Windows:%JDK_HOME%\jre\lib\security\

5)

#

# List of providers and their preference orders:

#

security.provider.1=sun.security.provider.Sun

security.provider.2=com.sun.rsajca.Provider

改为:

#

# List of providers and their preference orders:

#

security.provider.1=com.sun.crypto.provider.SunJCE

security.provider.2=sun.security.provider.Sun

security.provider.3=com.sun.rsajca.Provider

security.provider.4=com.sun.net.ssl.internal.ssl.Provider


HttpClient还要求安装commons-logging,下面跟httpclient一块安装。

3、取得源码


cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login

password: anoncvs

cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/logging

cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout jakarta-commons/httpclient


编译:

cd jakarta-commons/logging

ant dist

cp dis/*.jar ../httpclient/lib/

cd ../httpclient

ant dist


4、使用HttpClient编程的基本步聚



  1. 创建 HttpClient 的一个实例.
  2. 创建某个方法(DeleteMethod,EntityEnclosingMethod,ExpectContinueMethod,GetMethod,HeadMethod,MultipartPostMethod,OptionsMethod,PostMethod,PutMethod,TraceMethod)的一个实例,一般可用要目标URL为参数。
  3. 让 HttpClient 执行这个方法.
  4. 读取应答信息.
  5. 释放连接.
  6. 处理应答.


在执行方法的过程中,有两种异常,一种是HttpRecoverableException,表示偶然性错误发生,一般再试可能成功,另一种是IOException,严重错误。

这儿有这个教程中的一个例程,可以​​下载​​。


下面学习与http客户端相关的认证、重定向等内容。



1、认证


HttpClient三种不同的认证方案: Basic, Digest and NTLM. 这些方案可用于服务器或代理对客户端的认证,简称服务器认证或代理认证。

1)服务器认证(Server Authentication)

HttpClient处理服务器认证几乎是透明的,仅需要开发人员提供登录信息(login credentials)。登录信息保存在HttpState类的实例中,可以通过 setCredentials(String realm, Credentials cred)和getCredentials(String realm)来获取或设置。注意,设定对非特定站点访问所需要的登录信息,将realm参数置为null. HttpClient内建的自动认证,可以通过HttpMethod类的setDoAuthentication(boolean doAuthentication)方法关闭,而且这次关闭只影响HttpMethod当前的实例。

抢先认证(Preemptive Authentication)可以通过下述方法打开.

client.getState().setAuthenticationPreemptive(true);

在这种模式时,HttpClient会主动将basic认证应答信息传给服务器,即使在某种情况下服务器可能返回认证失败的应答,这样做主要是为了减少连接的建立。为使每个新建的 HttpState实例都实行抢先认证,可以如下设置系统属性。

setSystemProperty(Authenticator.PREEMPTIVE_PROPERTY, "true");


Httpclient实现的抢先认证遵循rfc2617.

2)代理认证(proxy authentication)

除了登录信息需单独存放以外,代理认证与服务器认证几乎一致。用 setProxyCredentials(String realm, Credentials cred)和 getProxyCredentials(String realm)设、取登录信息。

3)认证方案(authentication schemes)

Basic

是HTTP中规定最早的也是最兼容(?)的方案,遗憾的是也是最不安全的一个方案,因为它以明码传送用户名和密码。它要求一个UsernamePasswordCredentials实例,可以指定服务器端的访问空间或采用默认的登录信息。

Digest

是在HTTP1.1中增加的一个方案,虽然不如Basic得到的软件支持多,但还是有广泛的使用。Digest方案比Basic方案安全得多,因它根本就不通过网络传送实际的密码,传送的是利用这个密码对从服务器传来的一个随机数(nonce)的加密串。它要求一个UsernamePasswordCredentials实例,可以指定服务器端的访问空间或采用默认的登录信息。

NTLM

这是HttpClient支持的最复杂的认证协议。它M$设计的一个私有协议,没有公开的规范说明。一开始由于设计的缺陷,NTLM的安全性比Digest差,后来经过一个ServicePack补丁后,安全性则比较Digest高。NTLM需要一个NTCredentials实例. 注意,由于NTLM不使用访问空间(realms)的概念,HttpClient利用服务器的域名作访问空间的名字。还需要注意,提供给NTCredentials的用户名,不要用域名的前缀 - 如: "adrian" 是正确的,而 "DOMAIN\adrian" 则是错的.

NTLM认证的工作机制与basic和digest有很大的差别。这些差别一般由HttpClient处理,但理解这些差别有助避免在使用NTLM认证时出现错误。


  1. 从HttpClientAPI的角度来看,NTLM与其它认证方式一样的工作,差别是需要提供'NTCredentials'实例而不是'UsernamePasswordCredentials'(其实,前者只是扩展了后者)
  2. 对NTLM认证,访问空间是连接到的机器的域名,这对多域名主机会有一些麻烦.只有HttpClient连接中指定的域名才是认证用的域名。建议将realm设为null以使用默认的设置。
  3. NTLM只是认证了一个连接而不是一请求,所以每当一个新的连接建立就要进行一次认证,且在认证的过程中保持连接是非常重要的。 因此,NTLM不能同时用于代理认证和服务器认证,也不能用于http1.0连接或服务器不支持持久连接的情况。


2、重定向


由于技术限制,以及为保证2.0发布版API的稳定,HttpClient还不能自动处重定向,但对重定向到同一主机、同一端口且采用同一协议的情况HttpClient可以支持。不能自动的处理的情况,包括需要人工交互的情况,或超出httpclient的能力。

当服务器重定向指令指到不同的主机时,HttpClient只是简单地将重定向状态码作为应答状态。所有的300到399(包含两端)的返回码,都表示是重定向应答。常见的有:


  1. 301 永久移动. HttpStatus.SC_MOVED_PERMANENTLY
  2. 302 临时移动. HttpStatus.SC_MOVED_TEMPORARILY
  3. 303 See Other. HttpStatus.SC_SEE_OTHER
  4. 307 临时重定向. HttpStatus.SC_TEMPORARY_REDIRECT


当收到简单的重定向时,程序应从HttpMethod对象中抽取新的URL并将其下载。另外,限制一下重定向次数是个好的主意,这可以避免递归循环。新的URL可以从头字段Location中抽取,如下:

String redirectLocation;

Header locationHeader = method.getResponseHeader("location");

if (locationHeader != null) {

redirectLocation = locationHeader.getValue();

} else {

// The response is invalid and did not provide the new location for

// the resource. Report an error or possibly handle the response

// like a 404 Not Found error.

}


特殊重定向:


  1. 300 多重选择. HttpStatus.SC_MULTIPLE_CHOICES
  2. 304 没有改动. HttpStatus.SC_NO T_MODIFIED
  3. 305 使用代理. HttpStatus.SC_USE_PROXY