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