• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
init(final InputStream instream, int buffersize, final HttpParams params)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 
fillBuffer()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 
hasBufferedData()113     protected boolean hasBufferedData() {
114         return this.bufferpos < this.bufferlen;
115     }
116 
read()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 
read(final byte[] b, int off, int len)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 
read(final byte[] b)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 
locateLF()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 
readLine(final CharArrayBuffer charbuffer)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 
lineFromLineBuffer(final CharArrayBuffer charbuffer)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 
lineFromReadBuffer(final CharArrayBuffer charbuffer, int pos)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 
readLine()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 
getMetrics()271     public HttpTransportMetrics getMetrics() {
272         return this.metrics;
273     }
274 
275 }
276