HttpClient使用过程中的安全隐患

HttpClient使用过程中的安全隐患,这个有些标题党。因为这本身不是HttpClient的问题,而是使用者的问题。

安全隐患场景说明:
一旦请求大数据资源,则HttpClient线程会被长时间占有。即便调用了org.apache.commons.httpclient.HttpMethod#releaseConnection()方法,也无济于事。
如果请求的资源是应用可控的,那么不存在任何问题。可是恰恰我们应用的使用场景是,请求资源由用户自行输入,于是乎,我们不得不重视这个问题。

我们跟踪releaseConnection代码发现:
org.apache.commons.httpclient.HttpMethodBase#releaseConnection()

 1   public   void  releaseConnection() {
 2        try  {
 3            if  ( this .responseStream  !=   null ) {
 4                try  {
 5                    //  FYI - this may indirectly invoke responseBodyConsumed.
 6                    this .responseStream.close();
 7               }  catch  (IOException ignore) {
 8               }
 9           }
10       }  finally  {
11           ensureConnectionRelease();
12       }
13   }

org.apache.commons.httpclient.ChunkedInputStream#close()

 1   public   void  close()  throws  IOException {
 2        if  ( ! closed) {
 3            try  {
 4                if  ( ! eof) {
 5                   exhaustInputStream( this );
 6               }
 7           }  finally  {
 8               eof  =   true ;
 9               closed  =   true ;
10           }
11       }
12   }

org.apache.commons.httpclient.ChunkedInputStream#exhaustInputStream(InputStream inStream)

1   static   void  exhaustInputStream(InputStream inStream)  throws  IOException {
2        //  read and discard the remainder of the message
3        byte  buffer[]  =   new   byte [ 1024 ];
4        while  (inStream.read(buffer)  >=   0 ) {
5           ;
6       }
7   }

看到了吧,所谓的丢弃response,其实是读完了一次请求的response,只是不做任何处理罢了。

想想也是,HttpClient的设计理念是重复使用HttpConnection,岂能轻易被强制close呢。

怎么办?有朋友说,不是有time out设置嘛,设置下就可以下。
我先来解释下Httpclient中两个time out的概念:
1.public static final String CONNECTION_TIMEOUT = "http.connection.timeout";
即创建socket连接的超时时间:java.net.Socket#connect(SocketAddress endpoint, int timeout)中的timeout

2.public static final String SO_TIMEOUT = "http.socket.timeout";
即read data过程中,等待数据的timeout:java.net.Socket#setSoTimeout(int timeout)中的timeout

而在我上面场景中,这两个timeout都不满足,确实是由于资源过大,而占用了大量的请求时间。

问题总是要解决的,解决思路如下:
1.利用DelayQueue,管理所有请求
2.利用一个异步线程监控,关闭超长时间的请求

演示代码如下:

  1   public   class  Misc2 {
  2  
  3        private   static   final  DelayQueue < Timeout >  TIMEOUT_QUEUE  =   new  DelayQueue < Timeout > ();
  4  
  5        public   static   void  main(String[] args)  throws  Exception {
  6            new  Monitor().start();  //  超时监控线程
  7  
  8            new  Request( 4 ).start(); //  模拟第一个下载
  9            new  Request( 3 ).start(); //  模拟第二个下载
 10            new  Request( 2 ).start(); //  模拟第三个下载
 11       }
 12  
 13        /**
 14        * 模拟一次HttpClient请求
 15        * 
 16        *  @author  <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2011-4-9
 17         */
 18        public   static   class  Request  extends  Thread {
 19  
 20            private   long  delay;
 21  
 22            public  Request( long  delay){
 23                this .delay  =  delay;
 24           }
 25  
 26            public   void  run() {
 27               HttpClient hc  =   new  HttpClient();
 28               GetMethod req  =   new  GetMethod( " http://www.python.org/ftp/python/2.7.1/Python-2.7.1.tgz " );
 29                try  {
 30                   TIMEOUT_QUEUE.offer( new  Timeout(delay  *   1000 , hc.getHttpConnectionManager()));
 31                   hc.executeMethod(req);
 32               }  catch  (Exception e) {
 33                   System.out.println(e);
 34               }
 35               req.releaseConnection();
 36           }
 37  
 38       }
 39  
 40        /**
 41        * 监工:监控线程,通过DelayQueue,阻塞得到最近超时的对象,强制关闭
 42        * 
 43        *  @author  <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2011-4-9
 44         */
 45        public   static   class  Monitor  extends  Thread {
 46  
 47           @Override
 48            public   void  run() {
 49                while  ( true ) {
 50                    try  {
 51                       Timeout timeout  =  TIMEOUT_QUEUE.take();
 52                       timeout.forceClose();
 53                   }  catch  (InterruptedException e) {
 54                       System.out.println(e);
 55                   }
 56               }
 57           }
 58  
 59       }
 60  
 61        /**
 62        * 使用delay queue,对Delayed接口的实现 根据请求当前时间+该请求允许timeout时间,和当前时间比较,判断是否已经超时
 63        * 
 64        *  @author  <a href="mailto:li.jinl@alibaba-inc.com">Stone.J</a> 2011-4-9
 65         */
 66        public   static   class  Timeout  implements  Delayed {
 67  
 68            private   long                   debut;
 69            private   long                   delay;
 70            private  HttpConnectionManager manager;
 71  
 72            public  Timeout( long  delay, HttpConnectionManager manager){
 73                this .debut  =  System.currentTimeMillis();
 74                this .delay  =  delay;
 75                this .manager  =  manager;
 76           }
 77  
 78            public   void  forceClose() {
 79               System.out.println( this .debut  +   " : "   +   this .delay);
 80                if  (manager  instanceof  SimpleHttpConnectionManager) {
 81                   ((SimpleHttpConnectionManager) manager).shutdown();
 82               }
 83                if  (manager  instanceof  MultiThreadedHttpConnectionManager) {
 84                   ((MultiThreadedHttpConnectionManager) manager).shutdown();
 85               }
 86           }
 87  
 88           @Override
 89            public   int  compareTo(Delayed o) {
 90                if  (o  instanceof  Timeout) {
 91                   Timeout timeout  =  (Timeout) o;
 92                    if  ( this .debut  +   this .delay  ==  timeout.debut  +  timeout.delay) {
 93                        return   0 ;
 94                   }  else   if  ( this .debut  +   this .delay  >  timeout.debut  +  timeout.delay) {
 95                        return   1 ;
 96                   }  else  {
 97                        return   - 1 ;
 98                   }
 99               }
100                return   0 ;
101           }
102  
103           @Override
104            public   long  getDelay(TimeUnit unit) {
105                return  debut  +  delay  -  System.currentTimeMillis();
106           }
107  
108       }
109  
110   }



本来还想详细讲下DelayQueue,但是发现同事已经有比较纤细的描述,就加个链接吧 (人懒,没办法)

你可能感兴趣的:(apache,.net,python,socket,J#)