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 inputStream, final int bufferSize, final HttpParams params) {
94 Args.notNull(inputStream, "Input stream");
95 Args.notNegative(bufferSize, "Buffer size");
96 Args.notNull(params, "HTTP parameters");
97 this.inStream = inputStream;
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 readLen;
159 final int off = this.bufferLen;
160 final int len = this.buffer.length - off;
161 readLen = this.inStream.read(this.buffer, off, len);
162 if (readLen == -1) {
163 return -1;
164 }
165 this.bufferLen = off + readLen;
166 this.metrics.incrementBytesTransferred(readLen);
167 return readLen;
168 }
169
170 protected boolean hasBufferedData() {
171 return this.bufferPos < this.bufferLen;
172 }
173
174 @Override
175 public int read() throws IOException {
176 int noRead;
177 while (!hasBufferedData()) {
178 noRead = fillBuffer();
179 if (noRead == -1) {
180 return -1;
181 }
182 }
183 return this.buffer[this.bufferPos++] & 0xff;
184 }
185
186 @Override
187 public int read(final byte[] b, final int off, final int len) throws IOException {
188 if (b == null) {
189 return 0;
190 }
191 if (hasBufferedData()) {
192 final int chunk = Math.min(len, this.bufferLen - this.bufferPos);
193 System.arraycopy(this.buffer, this.bufferPos, b, off, chunk);
194 this.bufferPos += chunk;
195 return chunk;
196 }
197
198
199 if (len > this.minChunkLimit) {
200 final int read = this.inStream.read(b, off, len);
201 if (read > 0) {
202 this.metrics.incrementBytesTransferred(read);
203 }
204 return read;
205 }
206
207 while (!hasBufferedData()) {
208 final int noRead = fillBuffer();
209 if (noRead == -1) {
210 return -1;
211 }
212 }
213 final int chunk = Math.min(len, this.bufferLen - this.bufferPos);
214 System.arraycopy(this.buffer, this.bufferPos, b, off, chunk);
215 this.bufferPos += chunk;
216 return chunk;
217 }
218
219 @Override
220 public int read(final byte[] b) throws IOException {
221 if (b == null) {
222 return 0;
223 }
224 return read(b, 0, b.length);
225 }
226
227 private int locateLF() {
228 for (int i = this.bufferPos; i < this.bufferLen; i++) {
229 if (this.buffer[i] == HTTP.LF) {
230 return i;
231 }
232 }
233 return -1;
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251 @Override
252 public int readLine(final CharArrayBuffer charbuffer) throws IOException {
253 Args.notNull(charbuffer, "Char array buffer");
254 int noRead = 0;
255 boolean retry = true;
256 while (retry) {
257
258 final int i = locateLF();
259 if (i != -1) {
260
261 if (this.lineBuffer.isEmpty()) {
262
263 return lineFromReadBuffer(charbuffer, i);
264 }
265 retry = false;
266 final int len = i + 1 - this.bufferPos;
267 this.lineBuffer.append(this.buffer, this.bufferPos, len);
268 this.bufferPos = i + 1;
269 } else {
270
271 if (hasBufferedData()) {
272 final int len = this.bufferLen - this.bufferPos;
273 this.lineBuffer.append(this.buffer, this.bufferPos, len);
274 this.bufferPos = this.bufferLen;
275 }
276 noRead = fillBuffer();
277 if (noRead == -1) {
278 retry = false;
279 }
280 }
281 if (this.maxLineLen > 0 && this.lineBuffer.length() >= this.maxLineLen) {
282 throw new IOException("Maximum line length limit exceeded");
283 }
284 }
285 if (noRead == -1 && this.lineBuffer.isEmpty()) {
286
287 return -1;
288 }
289 return lineFromLineBuffer(charbuffer);
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305 private int lineFromLineBuffer(final CharArrayBuffer charbuffer)
306 throws IOException {
307
308 int len = this.lineBuffer.length();
309 if (len > 0) {
310 if (this.lineBuffer.byteAt(len - 1) == HTTP.LF) {
311 len--;
312 }
313
314 if (len > 0) {
315 if (this.lineBuffer.byteAt(len - 1) == HTTP.CR) {
316 len--;
317 }
318 }
319 }
320 if (this.ascii) {
321 charbuffer.append(this.lineBuffer, 0, len);
322 } else {
323 final ByteBuffer bbuf = ByteBuffer.wrap(this.lineBuffer.buffer(), 0, len);
324 len = appendDecoded(charbuffer, bbuf);
325 }
326 this.lineBuffer.clear();
327 return len;
328 }
329
330 private int lineFromReadBuffer(final CharArrayBuffer charbuffer, final int position)
331 throws IOException {
332 final int off = this.bufferPos;
333 int i = position;
334 this.bufferPos = i + 1;
335 if (i > off && this.buffer[i - 1] == HTTP.CR) {
336
337 i--;
338 }
339 int len = i - off;
340 if (this.ascii) {
341 charbuffer.append(this.buffer, off, len);
342 } else {
343 final ByteBuffer bbuf = ByteBuffer.wrap(this.buffer, off, len);
344 len = appendDecoded(charbuffer, bbuf);
345 }
346 return len;
347 }
348
349 private int appendDecoded(
350 final CharArrayBuffer charbuffer, final ByteBuffer bbuf) throws IOException {
351 if (!bbuf.hasRemaining()) {
352 return 0;
353 }
354 if (this.decoder == null) {
355 this.decoder = this.charset.newDecoder();
356 this.decoder.onMalformedInput(this.onMalformedCharAction);
357 this.decoder.onUnmappableCharacter(this.onUnmappableCharAction);
358 }
359 if (this.cbuf == null) {
360 this.cbuf = CharBuffer.allocate(1024);
361 }
362 this.decoder.reset();
363 int len = 0;
364 while (bbuf.hasRemaining()) {
365 final CoderResult result = this.decoder.decode(bbuf, this.cbuf, true);
366 len += handleDecodingResult(result, charbuffer, bbuf);
367 }
368 final CoderResult result = this.decoder.flush(this.cbuf);
369 len += handleDecodingResult(result, charbuffer, bbuf);
370 this.cbuf.clear();
371 return len;
372 }
373
374 private int handleDecodingResult(
375 final CoderResult result,
376 final CharArrayBuffer charbuffer,
377 final ByteBuffer bbuf) throws IOException {
378 if (result.isError()) {
379 result.throwException();
380 }
381 this.cbuf.flip();
382 final int len = this.cbuf.remaining();
383 while (this.cbuf.hasRemaining()) {
384 charbuffer.append(this.cbuf.get());
385 }
386 this.cbuf.compact();
387 return len;
388 }
389
390 @Override
391 public String readLine() throws IOException {
392 final CharArrayBuffertml#CharArrayBuffer">CharArrayBuffer charbuffer = new CharArrayBuffer(64);
393 final int readLen = readLine(charbuffer);
394 if (readLen != -1) {
395 return charbuffer.toString();
396 }
397 return null;
398 }
399
400 @Override
401 public HttpTransportMetrics getMetrics() {
402 return this.metrics;
403 }
404
405 }