http 协议

http 协议
rfc2616

对于返回是chunked格式的字节流的处理

import  java.io.IOException;
import  java.io.InputStream;
import  java.util.HashMap;
import  java.util.Map;
import  java.util.Set;
/**
 * http response chunked transfer type input stream
 * 
@author  jeffrey.ship
 *
 
*/
public   class  ChunkedInputStream {

    
public   static   final   int  CR  =   13 //  <US-ASCII CR, carriage return (13)>

    
public   static   final   int  LF  =   10 //  <US-ASCII LF, linefeed (10)>

    
public   static   final   int  SP  =   32 //  <US-ASCII SP, space (32)>

    
public   static   final   int  HT  =   9 //  <US-ASCII HT, horizontal-tab (9)>

    
private  InputStream is;

    
private  StringBuffer stringbuffer; //  share buffer

    
private  Map < String, String >  headerFields;

    
private   int  responseCode;

    
private  String responseMsg;

    
private   int  bytesleft;  //  Number of bytes left in current chunk

    
private   int  bytesread;  //  Number of bytes read since the stream was opened

    
private   boolean  chunked;  //  True if Transfer-Encoding: chunked

    
private   boolean  eof;  //  True if EOF seen

    
public  ChunkedInputStream(InputStream is) {
        
this .is  =  is;
        headerFields 
=   new  HashMap < String, String > ();
        stringbuffer 
=   new  StringBuffer( 32 );
    }

    
public   void  init()  throws  Exception {
        readResponseMessage(is);
        readHeaders(is);

        
//  Determine if this is a chunked datatransfer and setup
        String te  =  (String) headerFields.get( " transfer-encoding " );
        
if  (te  !=   null   &&  te.equals( " chunked " )) {
            chunked 
=   true ;
            bytesleft 
=  readChunkSize();
            eof 
=  (bytesleft  ==   0 );
        }
    }

    
private   void  readResponseMessage(InputStream in)  throws  IOException {
        String line 
=  readLine(in);
        
int  httpEnd, codeEnd;

        responseCode 
=   - 1 ;
        responseMsg 
=   null ;

        malformed: {
            
if  (line  ==   null )
                
break  malformed;

            httpEnd 
=  line.indexOf( '   ' );

            
if  (httpEnd  <   0 )
                
break  malformed;

            String httpVer 
=  line.substring( 0 , httpEnd);

            
if  ( ! httpVer.startsWith( " HTTP " ))
                
break  malformed;

            
if  (line.length()  <=  httpEnd)
                
break  malformed;

            codeEnd 
=  line.substring(httpEnd  +   1 ).indexOf( '   ' );

            
if  (codeEnd  <   0 )
                
break  malformed;

            codeEnd 
+=  (httpEnd  +   1 );

            
if  (line.length()  <=  codeEnd)
                
break  malformed;

            
try  {
                responseCode 
=  Integer.parseInt(line.substring(httpEnd  +   1 , codeEnd));
            } 
catch  (NumberFormatException nfe) {
                
break  malformed;
            }

            responseMsg 
=  line.substring(codeEnd  +   1 );
            
return ;
        }

        
throw   new  IOException( " malformed response message " );
    }
    
    

    
public   int  getResponseCode() {
        
return  responseCode;
    }

    
public  String getResponseMsg() {
        
return  responseMsg;
    }

    
private   void  readHeaders(InputStream in)  throws  IOException {
        String line, key, value;
        
int  index;

        
for  (;;) {
            line 
=  readLine(in);

            
if  (line  ==   null   ||  line.equals( "" ))
                
return ;

            index 
=  line.indexOf( ' : ' );

            
if  (index  <   0 )
                
throw   new  IOException( " malformed header field " );

            key 
=  line.substring( 0 , index);

            
if  (key.length()  ==   0 )
                
throw   new  IOException( " malformed header field " );

            
if  (line.length()  <=  index  +   2 ) {
                value 
=   "" ;
            } 
else  {
                value 
=  line.substring(index  +   2 );
            }

            headerFields.put(toLowerCase(key), value);
        }
    }

    
public  String getResponseHeaders() {
        Set
< String >  keys  =  headerFields.keySet();
        StringBuffer headers 
=   new  StringBuffer();
        
for  (String key : keys) {
            String value 
=  headerFields.get(key);
            headers.append(key 
+   " : "   +  value  +   " \r\n " );
        }
        
return  headers.toString();
    }

    
public  String getHeaderField(String name) {
        
return  (String) headerFields.get(toLowerCase(name));
    }

    
/**   */
    
/**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an <code>int</code> in the range <code>0</code> to
     * <code>255</code>. If no byte is available because the end of the
     * stream has been reached, the value <code>-1</code> is returned. This
     * method blocks until input data is available, the end of the stream is
     * detected, or an exception is thrown.
     * 
     * <p>
     * A subclass must provide an implementation of this method.
     * 
     * 
@return  the next byte of data, or <code>-1</code> if the end of the
     *         stream is reached.
     * 
@exception  IOException
     *                if an I/O error occurs.
     
*/
    
public   int  read()  throws  IOException {

        
//  Be consistent about returning EOF once encountered.
         if  (eof) {
            
return   - 1 ;
        }

        
/** //*
             * If all the current chunk has been read and this is a chunked
             * transfer then read the next chunk length.
             
*/
        
if  (bytesleft  <=   0   &&  chunked) {
            readCRLF(); 
//  Skip trailing

            bytesleft 
=  readChunkSize();
            
if  (bytesleft  ==   0 ) {
                eof 
=   true ;
                
return   - 1 ;
            }
        }

        
int  ch  =  is.read();
        eof 
=  (ch  ==   - 1 );
        bytesleft
-- ;
        bytesread
++ ;
        
return  ch;
    }

    
/**   */
    
/**
     * Reads some number of bytes from the input stream and stores them into the
     * buffer array <code>b</code>. The number of bytes actually read is
     * returned as an integer. This method blocks until input data is available,
     * end of file is detected, or an exception is thrown. (For HTTP requests
     * where the content length has been specified in the response headers, the
     * size of the read may be reduced if there are fewer bytes left than the
     * size of the supplied input buffer.)
     * 
     * 
@param  b
     *            the buffer into which the data is read.
     * 
@return  the total number of bytes read into the buffer, or
     *         <code>-1</code> is there is no more data because the end of the
     *         stream has been reached.
     * 
@exception  IOException
     *                if an I/O error occurs.
     * 
@see  java.io.InputStream#read(byte[])
     
*/
    
public   int  read( byte [] b)  throws  IOException {
        
long  len  =  getLength();

        
if  (len  !=   - 1 ) {
            
//  More bytes are expected
            len  -=  bytesread;
        } 
else  {
            
//  Buffered reading in chunks
            len  =  b.length;
        }

        
if  (len  ==   0 ) {
            eof 
=   true ;
            
//  All expected bytes have been read
             return   - 1 ;
        }

        
return  read(b,  0 , ( int ) (len  <  b.length  ?  len : b.length));
    }

    
public   int  read( byte  b[],  int  off,  int  len)  throws  IOException {
        
if  (b  ==   null ) {
            
throw   new  NullPointerException();
        } 
else   if  (off  <   0   ||  len  <   0   ||  len  >  b.length  -  off) {
            
throw   new  IndexOutOfBoundsException();
        } 
else   if  (len  ==   0 ) {
            
return   0 ;
        }

        
int  c  =  read();
        
if  (c  ==   - 1 ) {
            
return   - 1 ;
        }
        b[off] 
=  ( byte ) c;

        
int  i  =   1 ;
        
try  {
            
for  (; i  <  len; i ++ ) {
                c 
=  read();
                
if  (c  ==   - 1 ) {
                    
break ;
                }
                b[off 
+  i]  =  ( byte ) c;
            }
        } 
catch  (IOException ee) {
        }
        
return  i;
    }

    
public   long  getLength() {
        
return  getHeaderFieldInt( " content-length " - 1 );
    }

    
public   int  getHeaderFieldInt(String name,  int  def) {
        
try  {
            
return  Integer.parseInt(getHeaderField(name));
        } 
catch  (Throwable t) {
        }

        
return  def;
    }

    
/**
     * Returns the number of bytes that can be read (or skipped over) from this
     * input stream without blocking by the next caller of a method for this
     * input stream.
     * 
     * This method simply returns the number of bytes left from a chunked
     * response from an HTTP 1.1 server.
     
*/
    
public   int  available()  throws  IOException {
        
return  bytesleft;
    }

    
/** //*
         * Read <cr><lf> from the InputStream. 
@exception  IOException is thrown
         * if either <CR> or <LF> is missing.
         
*/
    
private   void  readCRLF()  throws  IOException {
        
int  ch;

        ch 
=  is.read();
        
if  (ch  !=  CR) {
            
throw   new  IOException( " missing CRLF " );
        }

        ch 
=  is.read();
        
if  (ch  !=  LF) {
            
throw   new  IOException( " missing CRLF " );
        }
    }

    
public   void  close()  throws  IOException {
        is.close();
    }

    
private  String toLowerCase(String string) {

        
//  Uses the shared stringbuffer to create a lower case string.
        stringbuffer.setLength( 0 );

        
for  ( int  i  =   0 ; i  <  string.length(); i ++ ) {
            stringbuffer.append(Character.toLowerCase(string.charAt(i)));
        }

        
return  stringbuffer.toString();
    }

    
/** //*
         * Uses the shared stringbuffer to read a line terminated by <cr><lf>
         * and return it as string.
         
*/
    
private  String readLine(InputStream in) {
        
int  c;
        StringBuffer stringbuffer 
=   new  StringBuffer();
        stringbuffer.setLength(
0 );

        
for  (;;) {
            
try  {
                c 
=  in.read();
                
if  (c  <   0 ) {
                    
return   null ;
                }
            } 
catch  (IOException ioe) {
                
return   null ;
            }

            
if  (isEnd(c)) {
                
try  {
                    
//  LF
                    in.read();
                } 
catch  (IOException e) {
                    e.printStackTrace();
                }
                
break ;
            }
            stringbuffer.append((
char ) c);

        }

        
return  stringbuffer.toString();
    }

    
public   static   boolean  isEnd( int  ch) {
        
return  ch  ==  CR  ||  ch  ==  LF;
    }

    
/** //*
         * Read the chunk size from the input. It is a hex length followed by
         * optional headers (ignored) and terminated with <cr><lf>.
         
*/
    
private   int  readChunkSize()  throws  IOException {
        
int  size  =   - 1 ;
        
try  {
            String chunk 
=  readLine(is);
            
if  (chunk  ==   null ) {
                
throw   new  IOException( " No Chunk Size " );
            }

            
int  i;
            
for  (i  =   0 ; i  <  chunk.length(); i ++ ) {
                
char  ch  =  chunk.charAt(i);
                
if  (Character.digit(ch,  16 ==   - 1 )
                    
break ;
            }

            
/** //* look at extensions?.  */
            size 
=  Integer.parseInt(chunk.substring( 0 , i),  16 );

        } 
catch  (NumberFormatException e) {
            
throw   new  IOException( " Bogus chunk size " );
        }

        
return  size;
    }
}

你可能感兴趣的:(http 协议)