Home | History | Annotate | Download | only in io
      1 /*
      2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionInputBuffer.java $
      3  * $Revision: 576077 $
      4  * $Date: 2007-09-16 04:50:22 -0700 (Sun, 16 Sep 2007) $
      5  *
      6  * ====================================================================
      7  * Licensed to the Apache Software Foundation (ASF) under one
      8  * or more contributor license agreements.  See the NOTICE file
      9  * distributed with this work for additional information
     10  * regarding copyright ownership.  The ASF licenses this file
     11  * to you under the Apache License, Version 2.0 (the
     12  * "License"); you may not use this file except in compliance
     13  * with the License.  You may obtain a copy of the License at
     14  *
     15  *   http://www.apache.org/licenses/LICENSE-2.0
     16  *
     17  * Unless required by applicable law or agreed to in writing,
     18  * software distributed under the License is distributed on an
     19  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     20  * KIND, either express or implied.  See the License for the
     21  * specific language governing permissions and limitations
     22  * under the License.
     23  * ====================================================================
     24  *
     25  * This software consists of voluntary contributions made by many
     26  * individuals on behalf of the Apache Software Foundation.  For more
     27  * information on the Apache Software Foundation, please see
     28  * <http://www.apache.org/>.
     29  *
     30  */
     31 
     32 package org.apache.http.impl.io;
     33 
     34 import java.io.IOException;
     35 import java.io.InputStream;
     36 
     37 import org.apache.http.io.SessionInputBuffer;
     38 import org.apache.http.io.HttpTransportMetrics;
     39 import org.apache.http.params.CoreConnectionPNames;
     40 import org.apache.http.params.HttpParams;
     41 import org.apache.http.params.HttpProtocolParams;
     42 import org.apache.http.protocol.HTTP;
     43 import org.apache.http.util.ByteArrayBuffer;
     44 import org.apache.http.util.CharArrayBuffer;
     45 
     46 /**
     47  * Abstract base class for session input buffers that stream data
     48  * from a {@link InputStream}.
     49  *
     50  * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
     51  *
     52  */
     53 public abstract class AbstractSessionInputBuffer implements SessionInputBuffer {
     54 
     55     private InputStream instream;
     56     private byte[] buffer;
     57     private int bufferpos;
     58     private int bufferlen;
     59 
     60     private ByteArrayBuffer linebuffer = null;
     61 
     62     private String charset = HTTP.US_ASCII;
     63     private boolean ascii = true;
     64     private int maxLineLen = -1;
     65 
     66     private HttpTransportMetricsImpl metrics;
     67 
     68     protected void init(final InputStream instream, int buffersize, final HttpParams params) {
     69         if (instream == null) {
     70             throw new IllegalArgumentException("Input stream may not be null");
     71         }
     72         if (buffersize <= 0) {
     73             throw new IllegalArgumentException("Buffer size may not be negative or zero");
     74         }
     75         if (params == null) {
     76             throw new IllegalArgumentException("HTTP parameters may not be null");
     77         }
     78         this.instream = instream;
     79         this.buffer = new byte[buffersize];
     80         this.bufferpos = 0;
     81         this.bufferlen = 0;
     82         this.linebuffer = new ByteArrayBuffer(buffersize);
     83         this.charset = HttpProtocolParams.getHttpElementCharset(params);
     84         this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
     85                      || this.charset.equalsIgnoreCase(HTTP.ASCII);
     86         this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
     87         this.metrics = new HttpTransportMetricsImpl();
     88     }
     89 
     90     protected int fillBuffer() throws IOException {
     91         // compact the buffer if necessary
     92         if (this.bufferpos > 0) {
     93             int len = this.bufferlen - this.bufferpos;
     94             if (len > 0) {
     95                 System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
     96             }
     97             this.bufferpos = 0;
     98             this.bufferlen = len;
     99         }
    100         int l;
    101         int off = this.bufferlen;
    102         int len = this.buffer.length - off;
    103         l = this.instream.read(this.buffer, off, len);
    104         if (l == -1) {
    105             return -1;
    106         } else {
    107             this.bufferlen = off + l;
    108             this.metrics.incrementBytesTransferred(l);
    109             return l;
    110         }
    111     }
    112 
    113     protected boolean hasBufferedData() {
    114         return this.bufferpos < this.bufferlen;
    115     }
    116 
    117     public int read() throws IOException {
    118         int noRead = 0;
    119         while (!hasBufferedData()) {
    120             noRead = fillBuffer();
    121             if (noRead == -1) {
    122                 return -1;
    123             }
    124         }
    125         return this.buffer[this.bufferpos++] & 0xff;
    126     }
    127 
    128     public int read(final byte[] b, int off, int len) throws IOException {
    129         if (b == null) {
    130             return 0;
    131         }
    132         int noRead = 0;
    133         while (!hasBufferedData()) {
    134             noRead = fillBuffer();
    135             if (noRead == -1) {
    136                 return -1;
    137             }
    138         }
    139         int chunk = this.bufferlen - this.bufferpos;
    140         if (chunk > len) {
    141             chunk = len;
    142         }
    143         System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
    144         this.bufferpos += chunk;
    145         return chunk;
    146     }
    147 
    148     public int read(final byte[] b) throws IOException {
    149         if (b == null) {
    150             return 0;
    151         }
    152         return read(b, 0, b.length);
    153     }
    154 
    155     private int locateLF() {
    156         for (int i = this.bufferpos; i < this.bufferlen; i++) {
    157             if (this.buffer[i] == HTTP.LF) {
    158                 return i;
    159             }
    160         }
    161         return -1;
    162     }
    163 
    164     public int readLine(final CharArrayBuffer charbuffer) throws IOException {
    165         if (charbuffer == null) {
    166             throw new IllegalArgumentException("Char array buffer may not be null");
    167         }
    168         this.linebuffer.clear();
    169         int noRead = 0;
    170         boolean retry = true;
    171         while (retry) {
    172             // attempt to find end of line (LF)
    173             int i = locateLF();
    174             if (i != -1) {
    175                 // end of line found.
    176                 if (this.linebuffer.isEmpty()) {
    177                     // the entire line is preset in the read buffer
    178                     return lineFromReadBuffer(charbuffer, i);
    179                 }
    180                 retry = false;
    181                 int len = i + 1 - this.bufferpos;
    182                 this.linebuffer.append(this.buffer, this.bufferpos, len);
    183                 this.bufferpos = i + 1;
    184             } else {
    185                 // end of line not found
    186                 if (hasBufferedData()) {
    187                     int len = this.bufferlen - this.bufferpos;
    188                     this.linebuffer.append(this.buffer, this.bufferpos, len);
    189                     this.bufferpos = this.bufferlen;
    190                 }
    191                 noRead = fillBuffer();
    192                 if (noRead == -1) {
    193                     retry = false;
    194                 }
    195             }
    196             if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) {
    197                 throw new IOException("Maximum line length limit exceeded");
    198             }
    199         }
    200         if (noRead == -1 && this.linebuffer.isEmpty()) {
    201             // indicate the end of stream
    202             return -1;
    203         }
    204         return lineFromLineBuffer(charbuffer);
    205     }
    206 
    207     private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
    208             throws IOException {
    209         // discard LF if found
    210         int l = this.linebuffer.length();
    211         if (l > 0) {
    212             if (this.linebuffer.byteAt(l - 1) == HTTP.LF) {
    213                 l--;
    214                 this.linebuffer.setLength(l);
    215             }
    216             // discard CR if found
    217             if (l > 0) {
    218                 if (this.linebuffer.byteAt(l - 1) == HTTP.CR) {
    219                     l--;
    220                     this.linebuffer.setLength(l);
    221                 }
    222             }
    223         }
    224         l = this.linebuffer.length();
    225         if (this.ascii) {
    226             charbuffer.append(this.linebuffer, 0, l);
    227         } else {
    228             // This is VERY memory inefficient, BUT since non-ASCII charsets are
    229             // NOT meant to be used anyway, there's no point optimizing it
    230             String s = new String(this.linebuffer.buffer(), 0, l, this.charset);
    231             charbuffer.append(s);
    232         }
    233         return l;
    234     }
    235 
    236     private int lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos)
    237             throws IOException {
    238         int off = this.bufferpos;
    239         int len;
    240         this.bufferpos = pos + 1;
    241         // BEGIN android-changed
    242         // The first test below was fixed to not try to skip beyond the
    243         // start of the live part of the buffer.
    244         if (pos > off && this.buffer[pos - 1] == HTTP.CR) {
    245             // skip CR if found
    246             pos--;
    247         }
    248         // END android-changed
    249         len = pos - off;
    250         if (this.ascii) {
    251             charbuffer.append(this.buffer, off, len);
    252         } else {
    253             // This is VERY memory inefficient, BUT since non-ASCII charsets are
    254             // NOT meant to be used anyway, there's no point optimizing it
    255             String s = new String(this.buffer, off, len, this.charset);
    256             charbuffer.append(s);
    257         }
    258         return len;
    259     }
    260 
    261     public String readLine() throws IOException {
    262         CharArrayBuffer charbuffer = new CharArrayBuffer(64);
    263         int l = readLine(charbuffer);
    264         if (l != -1) {
    265             return charbuffer.toString();
    266         } else {
    267             return null;
    268         }
    269     }
    270 
    271     public HttpTransportMetrics getMetrics() {
    272         return this.metrics;
    273     }
    274 
    275 }
    276