mobile/android/thirdparty/ch/boye/httpclientandroidlib/impl/io/AbstractSessionInputBuffer.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     1 /*
     2  * ====================================================================
     3  * Licensed to the Apache Software Foundation (ASF) under one
     4  * or more contributor license agreements.  See the NOTICE file
     5  * distributed with this work for additional information
     6  * regarding copyright ownership.  The ASF licenses this file
     7  * to you under the Apache License, Version 2.0 (the
     8  * "License"); you may not use this file except in compliance
     9  * with the License.  You may obtain a copy of the License at
    10  *
    11  *   http://www.apache.org/licenses/LICENSE-2.0
    12  *
    13  * Unless required by applicable law or agreed to in writing,
    14  * software distributed under the License is distributed on an
    15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    16  * KIND, either express or implied.  See the License for the
    17  * specific language governing permissions and limitations
    18  * under the License.
    19  * ====================================================================
    20  *
    21  * This software consists of voluntary contributions made by many
    22  * individuals on behalf of the Apache Software Foundation.  For more
    23  * information on the Apache Software Foundation, please see
    24  * <http://www.apache.org/>.
    25  *
    26  */
    28 package ch.boye.httpclientandroidlib.impl.io;
    30 import java.io.IOException;
    31 import java.io.InputStream;
    33 import ch.boye.httpclientandroidlib.io.BufferInfo;
    34 import ch.boye.httpclientandroidlib.io.SessionInputBuffer;
    35 import ch.boye.httpclientandroidlib.io.HttpTransportMetrics;
    36 import ch.boye.httpclientandroidlib.params.CoreConnectionPNames;
    37 import ch.boye.httpclientandroidlib.params.HttpParams;
    38 import ch.boye.httpclientandroidlib.params.HttpProtocolParams;
    39 import ch.boye.httpclientandroidlib.protocol.HTTP;
    40 import ch.boye.httpclientandroidlib.util.ByteArrayBuffer;
    41 import ch.boye.httpclientandroidlib.util.CharArrayBuffer;
    43 /**
    44  * Abstract base class for session input buffers that stream data from
    45  * an arbitrary {@link InputStream}. This class buffers input data in
    46  * an internal byte array for optimal input performance.
    47  * <p>
    48  * {@link #readLine(CharArrayBuffer)} and {@link #readLine()} methods of this
    49  * class treat a lone LF as valid line delimiters in addition to CR-LF required
    50  * by the HTTP specification.
    51  *
    52  * <p>
    53  * The following parameters can be used to customize the behavior of this
    54  * class:
    55  * <ul>
    56  *  <li>{@link ch.boye.httpclientandroidlib.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
    57  *  <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
    58  *  <li>{@link ch.boye.httpclientandroidlib.params.CoreConnectionPNames#MIN_CHUNK_LIMIT}</li>
    59  * </ul>
    60  * @since 4.0
    61  */
    62 public abstract class AbstractSessionInputBuffer implements SessionInputBuffer, BufferInfo {
    64     private InputStream instream;
    65     private byte[] buffer;
    66     private int bufferpos;
    67     private int bufferlen;
    69     private ByteArrayBuffer linebuffer = null;
    71     private String charset = HTTP.US_ASCII;
    72     private boolean ascii = true;
    73     private int maxLineLen = -1;
    74     private int minChunkLimit = 512;
    76     private HttpTransportMetricsImpl metrics;
    78     /**
    79      * Initializes this session input buffer.
    80      *
    81      * @param instream the source input stream.
    82      * @param buffersize the size of the internal buffer.
    83      * @param params HTTP parameters.
    84      */
    85     protected void init(final InputStream instream, int buffersize, final HttpParams params) {
    86         if (instream == null) {
    87             throw new IllegalArgumentException("Input stream may not be null");
    88         }
    89         if (buffersize <= 0) {
    90             throw new IllegalArgumentException("Buffer size may not be negative or zero");
    91         }
    92         if (params == null) {
    93             throw new IllegalArgumentException("HTTP parameters may not be null");
    94         }
    95         this.instream = instream;
    96         this.buffer = new byte[buffersize];
    97         this.bufferpos = 0;
    98         this.bufferlen = 0;
    99         this.linebuffer = new ByteArrayBuffer(buffersize);
   100         this.charset = HttpProtocolParams.getHttpElementCharset(params);
   101         this.ascii = this.charset.equalsIgnoreCase(HTTP.US_ASCII)
   102                      || this.charset.equalsIgnoreCase(HTTP.ASCII);
   103         this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
   104         this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512);
   105         this.metrics = createTransportMetrics();
   106     }
   108     /**
   109      * @since 4.1
   110      */
   111     protected HttpTransportMetricsImpl createTransportMetrics() {
   112         return new HttpTransportMetricsImpl();
   113     }
   115     /**
   116      * @since 4.1
   117      */
   118     public int capacity() {
   119         return this.buffer.length;
   120     }
   122     /**
   123      * @since 4.1
   124      */
   125     public int length() {
   126         return this.bufferlen - this.bufferpos;
   127     }
   129     /**
   130      * @since 4.1
   131      */
   132     public int available() {
   133         return capacity() - length();
   134     }
   136     protected int fillBuffer() throws IOException {
   137         // compact the buffer if necessary
   138         if (this.bufferpos > 0) {
   139             int len = this.bufferlen - this.bufferpos;
   140             if (len > 0) {
   141                 System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
   142             }
   143             this.bufferpos = 0;
   144             this.bufferlen = len;
   145         }
   146         int l;
   147         int off = this.bufferlen;
   148         int len = this.buffer.length - off;
   149         l = this.instream.read(this.buffer, off, len);
   150         if (l == -1) {
   151             return -1;
   152         } else {
   153             this.bufferlen = off + l;
   154             this.metrics.incrementBytesTransferred(l);
   155             return l;
   156         }
   157     }
   159     protected boolean hasBufferedData() {
   160         return this.bufferpos < this.bufferlen;
   161     }
   163     public int read() throws IOException {
   164         int noRead = 0;
   165         while (!hasBufferedData()) {
   166             noRead = fillBuffer();
   167             if (noRead == -1) {
   168                 return -1;
   169             }
   170         }
   171         return this.buffer[this.bufferpos++] & 0xff;
   172     }
   174     public int read(final byte[] b, int off, int len) throws IOException {
   175         if (b == null) {
   176             return 0;
   177         }
   178         if (hasBufferedData()) {
   179             int chunk = Math.min(len, this.bufferlen - this.bufferpos);
   180             System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
   181             this.bufferpos += chunk;
   182             return chunk;
   183         }
   184         // If the remaining capacity is big enough, read directly from the
   185         // underlying input stream bypassing the buffer.
   186         if (len > this.minChunkLimit) {
   187             int read = this.instream.read(b, off, len);
   188             if (read > 0) {
   189                 this.metrics.incrementBytesTransferred(read);
   190             }
   191             return read;
   192         } else {
   193             // otherwise read to the buffer first
   194             while (!hasBufferedData()) {
   195                 int noRead = fillBuffer();
   196                 if (noRead == -1) {
   197                     return -1;
   198                 }
   199             }
   200             int chunk = Math.min(len, this.bufferlen - this.bufferpos);
   201             System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
   202             this.bufferpos += chunk;
   203             return chunk;
   204         }
   205     }
   207     public int read(final byte[] b) throws IOException {
   208         if (b == null) {
   209             return 0;
   210         }
   211         return read(b, 0, b.length);
   212     }
   214     private int locateLF() {
   215         for (int i = this.bufferpos; i < this.bufferlen; i++) {
   216             if (this.buffer[i] == HTTP.LF) {
   217                 return i;
   218             }
   219         }
   220         return -1;
   221     }
   223     /**
   224      * Reads a complete line of characters up to a line delimiter from this
   225      * session buffer into the given line buffer. The number of chars actually
   226      * read is returned as an integer. The line delimiter itself is discarded.
   227      * If no char is available because the end of the stream has been reached,
   228      * the value <code>-1</code> is returned. This method blocks until input
   229      * data is available, end of file is detected, or an exception is thrown.
   230      * <p>
   231      * This method treats a lone LF as a valid line delimiters in addition
   232      * to CR-LF required by the HTTP specification.
   233      *
   234      * @param      charbuffer   the line buffer.
   235      * @return     one line of characters
   236      * @exception  IOException  if an I/O error occurs.
   237      */
   238     public int readLine(final CharArrayBuffer charbuffer) throws IOException {
   239         if (charbuffer == null) {
   240             throw new IllegalArgumentException("Char array buffer may not be null");
   241         }
   242         int noRead = 0;
   243         boolean retry = true;
   244         while (retry) {
   245             // attempt to find end of line (LF)
   246             int i = locateLF();
   247             if (i != -1) {
   248                 // end of line found.
   249                 if (this.linebuffer.isEmpty()) {
   250                     // the entire line is preset in the read buffer
   251                     return lineFromReadBuffer(charbuffer, i);
   252                 }
   253                 retry = false;
   254                 int len = i + 1 - this.bufferpos;
   255                 this.linebuffer.append(this.buffer, this.bufferpos, len);
   256                 this.bufferpos = i + 1;
   257             } else {
   258                 // end of line not found
   259                 if (hasBufferedData()) {
   260                     int len = this.bufferlen - this.bufferpos;
   261                     this.linebuffer.append(this.buffer, this.bufferpos, len);
   262                     this.bufferpos = this.bufferlen;
   263                 }
   264                 noRead = fillBuffer();
   265                 if (noRead == -1) {
   266                     retry = false;
   267                 }
   268             }
   269             if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) {
   270                 throw new IOException("Maximum line length limit exceeded");
   271             }
   272         }
   273         if (noRead == -1 && this.linebuffer.isEmpty()) {
   274             // indicate the end of stream
   275             return -1;
   276         }
   277         return lineFromLineBuffer(charbuffer);
   278     }
   280     /**
   281      * Reads a complete line of characters up to a line delimiter from this
   282      * session buffer. The line delimiter itself is discarded. If no char is
   283      * available because the end of the stream has been reached,
   284      * <code>null</code> is returned. This method blocks until input data is
   285      * available, end of file is detected, or an exception is thrown.
   286      * <p>
   287      * This method treats a lone LF as a valid line delimiters in addition
   288      * to CR-LF required by the HTTP specification.
   289      *
   290      * @return HTTP line as a string
   291      * @exception  IOException  if an I/O error occurs.
   292      */
   293     private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
   294             throws IOException {
   295         // discard LF if found
   296         int l = this.linebuffer.length();
   297         if (l > 0) {
   298             if (this.linebuffer.byteAt(l - 1) == HTTP.LF) {
   299                 l--;
   300                 this.linebuffer.setLength(l);
   301             }
   302             // discard CR if found
   303             if (l > 0) {
   304                 if (this.linebuffer.byteAt(l - 1) == HTTP.CR) {
   305                     l--;
   306                     this.linebuffer.setLength(l);
   307                 }
   308             }
   309         }
   310         l = this.linebuffer.length();
   311         if (this.ascii) {
   312             charbuffer.append(this.linebuffer, 0, l);
   313         } else {
   314             // This is VERY memory inefficient, BUT since non-ASCII charsets are
   315             // NOT meant to be used anyway, there's no point optimizing it
   316             String s = new String(this.linebuffer.buffer(), 0, l, this.charset);
   317             l = s.length();
   318             charbuffer.append(s);
   319         }
   320         this.linebuffer.clear();
   321         return l;
   322     }
   324     private int lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos)
   325             throws IOException {
   326         int off = this.bufferpos;
   327         int len;
   328         this.bufferpos = pos + 1;
   329         if (pos > 0 && this.buffer[pos - 1] == HTTP.CR) {
   330             // skip CR if found
   331             pos--;
   332         }
   333         len = pos - off;
   334         if (this.ascii) {
   335             charbuffer.append(this.buffer, off, len);
   336         } else {
   337             // This is VERY memory inefficient, BUT since non-ASCII charsets are
   338             // NOT meant to be used anyway, there's no point optimizing it
   339             String s = new String(this.buffer, off, len, this.charset);
   340             charbuffer.append(s);
   341             len = s.length();
   342         }
   343         return len;
   344     }
   346     public String readLine() throws IOException {
   347         CharArrayBuffer charbuffer = new CharArrayBuffer(64);
   348         int l = readLine(charbuffer);
   349         if (l != -1) {
   350             return charbuffer.toString();
   351         } else {
   352             return null;
   353         }
   354     }
   356     public HttpTransportMetrics getMetrics() {
   357         return this.metrics;
   358     }
   360 }

mercurial