最近做接触java开发,在使用java的httpclient时遇到了两个问题,这两个问题都是关于digest鉴权的问题,需求主要是通过http对接服务器,服务器是嵌入式设备,开启了digest鉴权。
第一个问题:在使用postMethod 方法发送大文件时,httpclient报出来IO异常,抓包后发现,第一次发送的请求没有携带鉴权信息,服务器(嵌入式设备)接收完首部发现没有鉴权信息时直接返回了401并直接关闭了连接。这时由于文件比较大,还没有全部发送出去,而socket连接被服务器关闭了,httpclient内部将http主体写入socket时抛出了IO异常。简单百度了一下,没有搜索到解决方案,而项目不能等,看了一下源码流程,暂时规避该问题。规避方式如下:
1 PostMethod postMethod = new PostMethod(url){ 2 Integer flag = 0; 3 protected boolean writeRequestBody(HttpState state, HttpConnection conn) throws IOException, HttpException { 4 if (flag == 0){ 5 flag = 1; 6 return true; 7 }else { 8 return super.writeRequestBody(state, conn); 9 } 10 } 11 };
重写了PostMethod类的writeRequestBody方法,在第一次调用该方法时,不写消息体直接返回。由于第一次发送是不携带鉴权信息,服务器必然返回401,因此不写消息体也没有影响,第二次携带鉴权信息时,调用原来的writeRequestBody方法正常写入消息体就能正常发送数据了。该临时规避的方式存在一个问题,请求必须是需要鉴权的,否则该请求发送的主体回为空。
第二个问题:由于嵌入式设备实现问题,httpclient每一个请求都需要登录一次,服务器(设备)都需要生成一个nonce。当httpclient较为频繁的调用时,服务端允许存在的nonce达到上限,所有请求直接失败,过一段时间后恢复正常。之后一直间歇性失败。一开始尝试使用同一个httpclient发起所有请求,抓包发现这样还是每个请求都需要重新发起一次鉴权,仅仅使用了http长连接。网上也没有收到解决方案。看了下源码找了一个规避方案,规避方案如下:
final AuthState tmpState = new AuthState();
GetMethod Get = new GetMethod(url){ public AuthState getHostAuthState(){ return tmpState; } };
对httpclient进行封装,在内部定义一个AuthState 类的实例。并重写GetMethod类的getHostAuthState方法(其他http请求方法同理),重写的getHostAuthState返回我们自己定义的AuthState 实例。该实例是用来保存鉴权信息的,当服务端返回401时,会携带鉴权需要的信息,如:nonce等,httpclient会解析这些信息并保存在AuthState 中,而这个实例本身是 定义在GetMethod类中的,这样每个请求一开始都无法获取上一个请求发起时服务端返回回来的鉴权信息,需要重新服务器重新返回鉴权信息。重写后的XXXMethod获取的都是同一个AuthState ,在服务器返回的nonce失效前都可以重复使用,不用不停生成新的nonce。
以上两个问题是使用httpclient发起请求时遇到的问题,在网上没有收到解决方案,因此记录一下。java也是开始接触,也许有其他更好的方式,如果有知道的,请告知一下,谢谢!
另外:问题是在工作中遇到的,由于公司政策的问题,不方便放源码,只能记录一下大概思路。