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