1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl.io;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.nio.ByteBuffer;
33 import java.nio.CharBuffer;
34 import java.nio.charset.Charset;
35 import java.nio.charset.CharsetDecoder;
36 import java.nio.charset.CoderResult;
37 import java.nio.charset.CodingErrorAction;
38
39 import org.apache.http.Consts;
40 import org.apache.http.io.BufferInfo;
41 import org.apache.http.io.HttpTransportMetrics;
42 import org.apache.http.io.SessionInputBuffer;
43 import org.apache.http.params.CoreConnectionPNames;
44 import org.apache.http.params.CoreProtocolPNames;
45 import org.apache.http.params.HttpParams;
46 import org.apache.http.protocol.HTTP;
47 import org.apache.http.util.Args;
48 import org.apache.http.util.ByteArrayBuffer;
49 import org.apache.http.util.CharArrayBuffer;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 @Deprecated
65 public abstract class AbstractSessionInputBuffer implements SessionInputBuffer, BufferInfo {
66
67 private InputStream instream;
68 private byte[] buffer;
69 private ByteArrayBuffer linebuffer;
70 private Charset charset;
71 private boolean ascii;
72 private int maxLineLen;
73 private int minChunkLimit;
74 private HttpTransportMetricsImpl metrics;
75 private CodingErrorAction onMalformedCharAction;
76 private CodingErrorAction onUnmappableCharAction;
77
78 private int bufferpos;
79 private int bufferlen;
80 private CharsetDecoder decoder;
81 private CharBuffer cbuf;
82
83 public AbstractSessionInputBuffer() {
84 }
85
86
87
88
89
90
91
92
93 protected void init(final InputStream instream, final int buffersize, final HttpParams params) {
94 Args.notNull(instream, "Input stream");
95 Args.notNegative(buffersize, "Buffer size");
96 Args.notNull(params, "HTTP parameters");
97 this.instream = instream;
98 this.buffer = new byte[buffersize];
99 this.bufferpos = 0;
100 this.bufferlen = 0;
101 this.linebuffer = new ByteArrayBuffer(buffersize);
102 final String charset = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
103 this.charset = charset != null ? Charset.forName(charset) : Consts.ASCII;
104 this.ascii = this.charset.equals(Consts.ASCII);
105 this.decoder = null;
106 this.maxLineLen = params.getIntParameter(CoreConnectionPNames.MAX_LINE_LENGTH, -1);
107 this.minChunkLimit = params.getIntParameter(CoreConnectionPNames.MIN_CHUNK_LIMIT, 512);
108 this.metrics = createTransportMetrics();
109 final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
110 CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
111 this.onMalformedCharAction = a1 != null ? a1 : CodingErrorAction.REPORT;
112 final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
113 CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
114 this.onUnmappableCharAction = a2 != null? a2 : CodingErrorAction.REPORT;
115 }
116
117
118
119
120 protected HttpTransportMetricsImpl createTransportMetrics() {
121 return new HttpTransportMetricsImpl();
122 }
123
124
125
126
127 @Override
128 public int capacity() {
129 return this.buffer.length;
130 }
131
132
133
134
135 @Override
136 public int length() {
137 return this.bufferlen - this.bufferpos;
138 }
139
140
141
142
143 @Override
144 public int available() {
145 return capacity() - length();
146 }
147
148 protected int fillBuffer() throws IOException {
149
150 if (this.bufferpos > 0) {
151 final int len = this.bufferlen - this.bufferpos;
152 if (len > 0) {
153 System.arraycopy(this.buffer, this.bufferpos, this.buffer, 0, len);
154 }
155 this.bufferpos = 0;
156 this.bufferlen = len;
157 }
158 final int l;
159 final int off = this.bufferlen;
160 final int len = this.buffer.length - off;
161 l = this.instream.read(this.buffer, off, len);
162 if (l == -1) {
163 return -1;
164 } else {
165 this.bufferlen = off + l;
166 this.metrics.incrementBytesTransferred(l);
167 return l;
168 }
169 }
170
171 protected boolean hasBufferedData() {
172 return this.bufferpos < this.bufferlen;
173 }
174
175 @Override
176 public int read() throws IOException {
177 int noRead;
178 while (!hasBufferedData()) {
179 noRead = fillBuffer();
180 if (noRead == -1) {
181 return -1;
182 }
183 }
184 return this.buffer[this.bufferpos++] & 0xff;
185 }
186
187 @Override
188 public int read(final byte[] b, final int off, final int len) throws IOException {
189 if (b == null) {
190 return 0;
191 }
192 if (hasBufferedData()) {
193 final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
194 System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
195 this.bufferpos += chunk;
196 return chunk;
197 }
198
199
200 if (len > this.minChunkLimit) {
201 final int read = this.instream.read(b, off, len);
202 if (read > 0) {
203 this.metrics.incrementBytesTransferred(read);
204 }
205 return read;
206 } else {
207
208 while (!hasBufferedData()) {
209 final int noRead = fillBuffer();
210 if (noRead == -1) {
211 return -1;
212 }
213 }
214 final int chunk = Math.min(len, this.bufferlen - this.bufferpos);
215 System.arraycopy(this.buffer, this.bufferpos, b, off, chunk);
216 this.bufferpos += chunk;
217 return chunk;
218 }
219 }
220
221 @Override
222 public int read(final byte[] b) throws IOException {
223 if (b == null) {
224 return 0;
225 }
226 return read(b, 0, b.length);
227 }
228
229 private int locateLF() {
230 for (int i = this.bufferpos; i < this.bufferlen; i++) {
231 if (this.buffer[i] == HTTP.LF) {
232 return i;
233 }
234 }
235 return -1;
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253 @Override
254 public int readLine(final CharArrayBuffer charbuffer) throws IOException {
255 Args.notNull(charbuffer, "Char array buffer");
256 int noRead = 0;
257 boolean retry = true;
258 while (retry) {
259
260 final int i = locateLF();
261 if (i != -1) {
262
263 if (this.linebuffer.isEmpty()) {
264
265 return lineFromReadBuffer(charbuffer, i);
266 }
267 retry = false;
268 final int len = i + 1 - this.bufferpos;
269 this.linebuffer.append(this.buffer, this.bufferpos, len);
270 this.bufferpos = i + 1;
271 } else {
272
273 if (hasBufferedData()) {
274 final int len = this.bufferlen - this.bufferpos;
275 this.linebuffer.append(this.buffer, this.bufferpos, len);
276 this.bufferpos = this.bufferlen;
277 }
278 noRead = fillBuffer();
279 if (noRead == -1) {
280 retry = false;
281 }
282 }
283 if (this.maxLineLen > 0 && this.linebuffer.length() >= this.maxLineLen) {
284 throw new IOException("Maximum line length limit exceeded");
285 }
286 }
287 if (noRead == -1 && this.linebuffer.isEmpty()) {
288
289 return -1;
290 }
291 return lineFromLineBuffer(charbuffer);
292 }
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307 private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
308 throws IOException {
309
310 int len = this.linebuffer.length();
311 if (len > 0) {
312 if (this.linebuffer.byteAt(len - 1) == HTTP.LF) {
313 len--;
314 }
315
316 if (len > 0) {
317 if (this.linebuffer.byteAt(len - 1) == HTTP.CR) {
318 len--;
319 }
320 }
321 }
322 if (this.ascii) {
323 charbuffer.append(this.linebuffer, 0, len);
324 } else {
325 final ByteBuffer bbuf = ByteBuffer.wrap(this.linebuffer.buffer(), 0, len);
326 len = appendDecoded(charbuffer, bbuf);
327 }
328 this.linebuffer.clear();
329 return len;
330 }
331
332 private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position)
333 throws IOException {
334 final int off = this.bufferpos;
335 int i = position;
336 this.bufferpos = i + 1;
337 if (i > off && this.buffer[i - 1] == HTTP.CR) {
338
339 i--;
340 }
341 int len = i - off;
342 if (this.ascii) {
343 charbuffer.append(this.buffer, off, len);
344 } else {
345 final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len);
346 len = appendDecoded(charbuffer, bbuf);
347 }
348 return len;
349 }
350
351 private int appendDecoded(
352 final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException {
353 if (!bbuf.hasRemaining()) {
354 return 0;
355 }
356 if (this.decoder == null) {
357 this.decoder = this.charset.newDecoder();
358 this.decoder.onMalformedInput(this.onMalformedCharAction);
359 this.decoder.onUnmappableCharacter(this.onUnmappableCharAction);
360 }
361 if (this.cbuf == null) {
362 this.cbuf = CharBuffer.allocate(1024);
363 }
364 this.decoder.reset();
365 int len = 0;
366 while (bbuf.hasRemaining()) {
367 final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true);
368 len += handleDecodingResult(result, charbuffer, bbuf);
369 }
370 final CoderResult result = this.decoder.flush(this.cbuf);
371 len += handleDecodingResult(result, charbuffer, bbuf);
372 this.cbuf.clear();
373 return len;
374 }
375
376 private int handleDecodingResult(
377 final CoderResult result,
378 final CharArrayBuffer charbuffer,
379 final ByteBuffer bbuf) throws IOException {
380 if (result.isError()) {
381 result.throwException();
382 }
383 this.cbuf.flip();
384 final int len = this.cbuf.remaining();
385 while (this.cbuf.hasRemaining()) {
386 charbuffer.append(this.cbuf.get());
387 }
388 this.cbuf.compact();
389 return len;
390 }
391
392 @Override
393 public String readLine() throws IOException {
394 final CharArrayBuffer charbuffer = new CharArrayBuffer(64);
395 final int l = readLine(charbuffer);
396 if (l != -1) {
397 return charbuffer.toString();
398 } else {
399 return null;
400 }
401 }
402
403 @Override
404 public HttpTransportMetrics getMetrics() {
405 return this.metrics;
406 }
407
408 }