001: /*
002: * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/httpcore/tags/4.0-beta1/module-main/src/main/java/org/apache/http/impl/io/AbstractSessionInputBuffer.java $
003: * $Revision: 576077 $
004: * $Date: 2007-09-16 13:50:22 +0200 (Sun, 16 Sep 2007) $
005: *
006: * ====================================================================
007: * Licensed to the Apache Software Foundation (ASF) under one
008: * or more contributor license agreements. See the NOTICE file
009: * distributed with this work for additional information
010: * regarding copyright ownership. The ASF licenses this file
011: * to you under the Apache License, Version 2.0 (the
012: * "License"); you may not use this file except in compliance
013: * with the License. You may obtain a copy of the License at
014: *
015: * http://www.apache.org/licenses/LICENSE-2.0
016: *
017: * Unless required by applicable law or agreed to in writing,
018: * software distributed under the License is distributed on an
019: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020: * KIND, either express or implied. See the License for the
021: * specific language governing permissions and limitations
022: * under the License.
023: * ====================================================================
024: *
025: * This software consists of voluntary contributions made by many
026: * individuals on behalf of the Apache Software Foundation. For more
027: * information on the Apache Software Foundation, please see
028: * <http://www.apache.org/>.
029: *
030: */
031:
032: package org.apache.http.impl.io;
033:
034: import java.io.IOException;
035: import java.io.InputStream;
036:
037: import org.apache.http.io.SessionInputBuffer;
038: import org.apache.http.io.HttpTransportMetrics;
039: import org.apache.http.params.CoreConnectionPNames;
040: import org.apache.http.params.HttpParams;
041: import org.apache.http.params.HttpProtocolParams;
042: import org.apache.http.protocol.HTTP;
043: import org.apache.http.util.ByteArrayBuffer;
044: import org.apache.http.util.CharArrayBuffer;
045:
046: /**
047: * Abstract base class for session input buffers that stream data
048: * from a {@link InputStream}.
049: *
050: * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
051: *
052: */
053: public abstract class AbstractSessionInputBuffer implements
054: SessionInputBuffer {
055:
056: private InputStream instream;
057: private byte[] buffer;
058: private int bufferpos;
059: private int bufferlen;
060:
061: private ByteArrayBuffer linebuffer = null;
062:
063: private String charset = HTTP.US_ASCII;
064: private boolean ascii = true;
065: private int maxLineLen = -1;
066:
067: private HttpTransportMetricsImpl metrics;
068:
069: protected void init(final InputStream instream, int buffersize,
070: final HttpParams params) {
071: if (instream == null) {
072: throw new IllegalArgumentException(
073: "Input stream may not be null");
074: }
075: if (buffersize <= 0) {
076: throw new IllegalArgumentException(
077: "Buffer size may not be negative or zero");
078: }
079: if (params == null) {
080: throw new IllegalArgumentException(
081: "HTTP parameters may not be null");
082: }
083: this .instream = instream;
084: this .buffer = new byte[buffersize];
085: this .bufferpos = 0;
086: this .bufferlen = 0;
087: this .linebuffer = new ByteArrayBuffer(buffersize);
088: this .charset = HttpProtocolParams.getHttpElementCharset(params);
089: this .ascii = this .charset.equalsIgnoreCase(HTTP.US_ASCII)
090: || this .charset.equalsIgnoreCase(HTTP.ASCII);
091: this .maxLineLen = params.getIntParameter(
092: CoreConnectionPNames.MAX_LINE_LENGTH, -1);
093: this .metrics = new HttpTransportMetricsImpl();
094: }
095:
096: protected int fillBuffer() throws IOException {
097: // compact the buffer if necessary
098: if (this .bufferpos > 0) {
099: int len = this .bufferlen - this .bufferpos;
100: if (len > 0) {
101: System.arraycopy(this .buffer, this .bufferpos,
102: this .buffer, 0, len);
103: }
104: this .bufferpos = 0;
105: this .bufferlen = len;
106: }
107: int l;
108: int off = this .bufferlen;
109: int len = this .buffer.length - off;
110: l = this .instream.read(this .buffer, off, len);
111: if (l == -1) {
112: return -1;
113: } else {
114: this .bufferlen = off + l;
115: this .metrics.incrementBytesTransferred(l);
116: return l;
117: }
118: }
119:
120: protected boolean hasBufferedData() {
121: return this .bufferpos < this .bufferlen;
122: }
123:
124: public int read() throws IOException {
125: int noRead = 0;
126: while (!hasBufferedData()) {
127: noRead = fillBuffer();
128: if (noRead == -1) {
129: return -1;
130: }
131: }
132: return this .buffer[this .bufferpos++] & 0xff;
133: }
134:
135: public int read(final byte[] b, int off, int len)
136: throws IOException {
137: if (b == null) {
138: return 0;
139: }
140: int noRead = 0;
141: while (!hasBufferedData()) {
142: noRead = fillBuffer();
143: if (noRead == -1) {
144: return -1;
145: }
146: }
147: int chunk = this .bufferlen - this .bufferpos;
148: if (chunk > len) {
149: chunk = len;
150: }
151: System.arraycopy(this .buffer, this .bufferpos, b, off, chunk);
152: this .bufferpos += chunk;
153: return chunk;
154: }
155:
156: public int read(final byte[] b) throws IOException {
157: if (b == null) {
158: return 0;
159: }
160: return read(b, 0, b.length);
161: }
162:
163: private int locateLF() {
164: for (int i = this .bufferpos; i < this .bufferlen; i++) {
165: if (this .buffer[i] == HTTP.LF) {
166: return i;
167: }
168: }
169: return -1;
170: }
171:
172: public int readLine(final CharArrayBuffer charbuffer)
173: throws IOException {
174: if (charbuffer == null) {
175: throw new IllegalArgumentException(
176: "Char array buffer may not be null");
177: }
178: this .linebuffer.clear();
179: int noRead = 0;
180: boolean retry = true;
181: while (retry) {
182: // attempt to find end of line (LF)
183: int i = locateLF();
184: if (i != -1) {
185: // end of line found.
186: if (this .linebuffer.isEmpty()) {
187: // the entire line is preset in the read buffer
188: return lineFromReadBuffer(charbuffer, i);
189: }
190: retry = false;
191: int len = i + 1 - this .bufferpos;
192: this .linebuffer
193: .append(this .buffer, this .bufferpos, len);
194: this .bufferpos = i + 1;
195: } else {
196: // end of line not found
197: if (hasBufferedData()) {
198: int len = this .bufferlen - this .bufferpos;
199: this .linebuffer.append(this .buffer, this .bufferpos,
200: len);
201: this .bufferpos = this .bufferlen;
202: }
203: noRead = fillBuffer();
204: if (noRead == -1) {
205: retry = false;
206: }
207: }
208: if (this .maxLineLen > 0
209: && this .linebuffer.length() >= this .maxLineLen) {
210: throw new IOException(
211: "Maximum line length limit exceeded");
212: }
213: }
214: if (noRead == -1 && this .linebuffer.isEmpty()) {
215: // indicate the end of stream
216: return -1;
217: }
218: return lineFromLineBuffer(charbuffer);
219: }
220:
221: private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
222: throws IOException {
223: // discard LF if found
224: int l = this .linebuffer.length();
225: if (l > 0) {
226: if (this .linebuffer.byteAt(l - 1) == HTTP.LF) {
227: l--;
228: this .linebuffer.setLength(l);
229: }
230: // discard CR if found
231: if (l > 0) {
232: if (this .linebuffer.byteAt(l - 1) == HTTP.CR) {
233: l--;
234: this .linebuffer.setLength(l);
235: }
236: }
237: }
238: l = this .linebuffer.length();
239: if (this .ascii) {
240: charbuffer.append(this .linebuffer, 0, l);
241: } else {
242: // This is VERY memory inefficient, BUT since non-ASCII charsets are
243: // NOT meant to be used anyway, there's no point optimizing it
244: String s = new String(this .linebuffer.buffer(), 0, l,
245: this .charset);
246: charbuffer.append(s);
247: }
248: return l;
249: }
250:
251: private int lineFromReadBuffer(final CharArrayBuffer charbuffer,
252: int pos) throws IOException {
253: int off = this .bufferpos;
254: int len;
255: this .bufferpos = pos + 1;
256: if (pos > 0 && this .buffer[pos - 1] == HTTP.CR) {
257: // skip CR if found
258: pos--;
259: }
260: len = pos - off;
261: if (this .ascii) {
262: charbuffer.append(this .buffer, off, len);
263: } else {
264: // This is VERY memory inefficient, BUT since non-ASCII charsets are
265: // NOT meant to be used anyway, there's no point optimizing it
266: String s = new String(this .buffer, off, len, this .charset);
267: charbuffer.append(s);
268: }
269: return len;
270: }
271:
272: public String readLine() throws IOException {
273: CharArrayBuffer charbuffer = new CharArrayBuffer(64);
274: int l = readLine(charbuffer);
275: if (l != -1) {
276: return charbuffer.toString();
277: } else {
278: return null;
279: }
280: }
281:
282: public HttpTransportMetrics getMetrics() {
283: return this.metrics;
284: }
285:
286: }
|