Source for gnu.inet.http.ChunkedInputStream

   1: /*
   2:  * ChunkedInputStream.java
   3:  * Copyright (C) 2004 The Free Software Foundation
   4:  * 
   5:  * This file is part of GNU inetlib, a library.
   6:  * 
   7:  * GNU inetlib is free software; you can redistribute it and/or modify
   8:  * it under the terms of the GNU General Public License as published by
   9:  * the Free Software Foundation; either version 2 of the License, or
  10:  * (at your option) any later version.
  11:  * 
  12:  * GNU inetlib is distributed in the hope that it will be useful,
  13:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15:  * GNU General Public License for more details.
  16:  * 
  17:  * You should have received a copy of the GNU General Public License
  18:  * along with this library; if not, write to the Free Software
  19:  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20:  * 
  21:  * Linking this library statically or dynamically with other modules is
  22:  * making a combined work based on this library.  Thus, the terms and
  23:  * conditions of the GNU General Public License cover the whole
  24:  * combination.
  25:  *
  26:  * As a special exception, the copyright holders of this library give you
  27:  * permission to link this library with independent modules to produce an
  28:  * executable, regardless of the license terms of these independent
  29:  * modules, and to copy and distribute the resulting executable under
  30:  * terms of your choice, provided that you also meet, for each linked
  31:  * independent module, the terms and conditions of the license of that
  32:  * module.  An independent module is a module which is not derived from
  33:  * or based on this library.  If you modify this library, you may extend
  34:  * this exception to your version of the library, but you are not
  35:  * obliged to do so.  If you do not wish to do so, delete this
  36:  * exception statement from your version.
  37:  */
  38: 
  39: package gnu.inet.http;
  40: 
  41: import java.io.FilterInputStream;
  42: import java.io.InputStream;
  43: import java.io.IOException;
  44: import java.net.ProtocolException;
  45: 
  46: /**
  47:  * Input stream wrapper for the "chunked" transfer-coding.
  48:  *
  49:  * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
  50:  */
  51: public class ChunkedInputStream
  52:   extends FilterInputStream
  53: {
  54: 
  55:   private static final byte CR = 0x0d;
  56:   private static final byte LF = 0x0a;
  57: 
  58:   int size;
  59:   int count;
  60:   boolean meta;
  61:   boolean eof;
  62:   Headers headers;
  63: 
  64:   /**
  65:    * Constructor.
  66:    * @param in the response socket input stream
  67:    * @param headers the headers to receive additional header lines
  68:    */
  69:   public ChunkedInputStream(InputStream in, Headers headers)
  70:   {
  71:     super(in);
  72:     this.headers = headers;
  73:     size = -1;
  74:     count = 0;
  75:     meta = true;
  76:   }
  77: 
  78:   public int read()
  79:     throws IOException
  80:   {
  81:     byte[] buf = new byte[1];
  82:     int len = read(buf, 0, 1);
  83:     if (len == -1)
  84:       {
  85:         return -1;
  86:       }
  87:     int ret = (int) buf[0];
  88:     if (ret < 0)
  89:       {
  90:         ret += 0x100;
  91:       }
  92:     return ret;
  93:   }
  94: 
  95:   public int read(byte[] buffer)
  96:     throws IOException
  97:   {
  98:     return read(buffer, 0, buffer.length);
  99:   }
 100: 
 101:   public int read(byte[] buffer, int offset, int length)
 102:     throws IOException
 103:   {
 104:     if (eof)
 105:       {
 106:         return -1;
 107:       }
 108:     if (meta)
 109:       {
 110:         // Read chunk header
 111:         int c, last = 0;
 112:         boolean seenSemi = false;
 113:         StringBuffer buf = new StringBuffer();
 114:         do
 115:           {
 116:             c = in.read();
 117:             if (c == 0x3b) // ;
 118:               {
 119:                 seenSemi = true;
 120:               }
 121:             else if (c == 0x0a && last == 0x0d) // CRLF
 122:               {
 123:                 size = Integer.parseInt(buf.toString(), 16);
 124:                 break;
 125:               }
 126:             else if (!seenSemi && c >= 0x30)
 127:               {
 128:                 buf.append ((char) c);
 129:               }
 130:             last = c;
 131:           }
 132:         while(c != -1);
 133:         count = 0;
 134:         meta = false;
 135:       }
 136:     if (size == 0)
 137:       {
 138:         // Read trailer
 139:         headers.parse(in);
 140:         eof = true;
 141:         return -1;
 142:       }
 143:     else
 144:       {
 145:         int diff = length - offset;
 146:         int max = size - count;
 147:         max = (diff < max) ? diff : max;
 148:         int len = (max > 0) ? in.read(buffer, offset, max) : 0;
 149:         count += len;
 150:         if (count == size)
 151:           {
 152:             // Read CRLF
 153:             int c1 = in.read();
 154:             int c2 = in.read();
 155:             if (c1 == -1 && c2 == -1)
 156:               {
 157:                 // EOF before CRLF: bad, but ignore
 158:                 eof = true;
 159:                 return -1;
 160:               }
 161:             if (c1 != 0x0d || c2 != 0x0a)
 162:               {
 163:                 throw new ProtocolException("expecting CRLF: " + c1 + "," + c2);
 164:               }
 165:             meta = true;
 166:           }
 167:         return len;
 168:       }
 169:   }
 170:   
 171: }