9910

单飞

   :: 首页 :: 联系 :: 聚合  :: 管理
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;
    }
}

posted on 2009-11-05 14:45 单飞 阅读(363) 评论(0)  编辑  收藏 所属分类: java

只有注册用户登录后才能发表评论。


网站导航: