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.nio.reactor;
29
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.CharBuffer;
33 import java.nio.channels.ReadableByteChannel;
34 import java.nio.channels.WritableByteChannel;
35 import java.nio.charset.CharacterCodingException;
36 import java.nio.charset.Charset;
37 import java.nio.charset.CharsetDecoder;
38 import java.nio.charset.CoderResult;
39 import java.nio.charset.CodingErrorAction;
40
41 import org.apache.http.MessageConstraintException;
42 import org.apache.http.config.MessageConstraints;
43 import org.apache.http.nio.reactor.SessionInputBuffer;
44 import org.apache.http.nio.util.ByteBufferAllocator;
45 import org.apache.http.nio.util.ExpandableBuffer;
46 import org.apache.http.nio.util.HeapByteBufferAllocator;
47 import org.apache.http.params.CoreProtocolPNames;
48 import org.apache.http.params.HttpParams;
49 import org.apache.http.protocol.HTTP;
50 import org.apache.http.util.Args;
51 import org.apache.http.util.CharArrayBuffer;
52 import org.apache.http.util.CharsetUtils;
53
54
55
56
57
58
59
60 @SuppressWarnings("deprecation")
61 public class SessionInputBufferImpl extends ExpandableBuffer implements SessionInputBuffer {
62
63 private final CharsetDecoder charDecoder;
64 private final MessageConstraints constraints;
65 private final int lineBufferSize;
66
67 private CharBuffer charBuffer;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public SessionInputBufferImpl(
85 final int bufferSize,
86 final int lineBufferSize,
87 final MessageConstraints constraints,
88 final CharsetDecoder charDecoder,
89 final ByteBufferAllocator allocator) {
90 super(bufferSize, allocator != null ? allocator : HeapByteBufferAllocator.INSTANCE);
91 this.lineBufferSize = Args.positive(lineBufferSize, "Line buffer size");
92 this.constraints = constraints != null ? constraints : MessageConstraints.DEFAULT;
93 this.charDecoder = charDecoder;
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 public SessionInputBufferImpl(
110 final int bufferSize,
111 final int lineBufferSize,
112 final CharsetDecoder charDecoder,
113 final ByteBufferAllocator allocator) {
114 this(bufferSize, lineBufferSize, null, charDecoder, allocator);
115 }
116
117
118
119
120
121
122 @Deprecated
123 public SessionInputBufferImpl(
124 final int bufferSize,
125 final int lineBufferSize,
126 final ByteBufferAllocator allocator,
127 final HttpParams params) {
128 super(bufferSize, allocator);
129 this.lineBufferSize = Args.positive(lineBufferSize, "Line buffer size");
130 final String charsetName = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
131 final Charset charset = CharsetUtils.lookup(charsetName);
132 if (charset != null) {
133 this.charDecoder = charset.newDecoder();
134 final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
135 CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
136 this.charDecoder.onMalformedInput(a1 != null ? a1 : CodingErrorAction.REPORT);
137 final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
138 CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
139 this.charDecoder.onUnmappableCharacter(a2 != null? a2 : CodingErrorAction.REPORT);
140 } else {
141 this.charDecoder = null;
142 }
143 this.constraints = MessageConstraints.DEFAULT;
144 }
145
146
147
148
149
150 @Deprecated
151 public SessionInputBufferImpl(
152 final int bufferSize,
153 final int lineBufferSize,
154 final HttpParams params) {
155 this(bufferSize, lineBufferSize, HeapByteBufferAllocator.INSTANCE, params);
156 }
157
158
159
160
161
162
163
164
165
166
167
168
169 public SessionInputBufferImpl(
170 final int bufferSize,
171 final int lineBufferSize,
172 final Charset charset) {
173 this(bufferSize, lineBufferSize, null,
174 charset != null ? charset.newDecoder() : null, HeapByteBufferAllocator.INSTANCE);
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 public SessionInputBufferImpl(
191 final int bufferSize,
192 final int lineBufferSize,
193 final MessageConstraints constraints,
194 final Charset charset) {
195 this(bufferSize, lineBufferSize, constraints,
196 charset != null ? charset.newDecoder() : null, HeapByteBufferAllocator.INSTANCE);
197 }
198
199
200
201
202
203
204
205
206
207 public SessionInputBufferImpl(
208 final int bufferSize,
209 final int lineBufferSize) {
210 this(bufferSize, lineBufferSize, null, null, HeapByteBufferAllocator.INSTANCE);
211 }
212
213
214
215
216
217
218
219
220 public SessionInputBufferImpl(final int bufferSize) {
221 this(bufferSize, 256, null, null, HeapByteBufferAllocator.INSTANCE);
222 }
223
224 @Override
225 public int fill(final ReadableByteChannel channel) throws IOException {
226 Args.notNull(channel, "Channel");
227 setInputMode();
228 if (!this.buffer.hasRemaining()) {
229 expand();
230 }
231 return channel.read(this.buffer);
232 }
233
234 @Override
235 public int read() {
236 setOutputMode();
237 return this.buffer.get() & 0xff;
238 }
239
240 @Override
241 public int read(final ByteBuffer dst, final int maxLen) {
242 if (dst == null) {
243 return 0;
244 }
245 setOutputMode();
246 final int len = Math.min(dst.remaining(), maxLen);
247 final int chunk = Math.min(this.buffer.remaining(), len);
248 if (this.buffer.remaining() > chunk) {
249 final int oldLimit = this.buffer.limit();
250 final int newLimit = this.buffer.position() + chunk;
251 this.buffer.limit(newLimit);
252 dst.put(this.buffer);
253 this.buffer.limit(oldLimit);
254 return len;
255 }
256 dst.put(this.buffer);
257 return chunk;
258 }
259
260 @Override
261 public int read(final ByteBuffer dst) {
262 if (dst == null) {
263 return 0;
264 }
265 return read(dst, dst.remaining());
266 }
267
268 @Override
269 public int read(final WritableByteChannel dst, final int maxLen) throws IOException {
270 if (dst == null) {
271 return 0;
272 }
273 setOutputMode();
274 final int bytesRead;
275 if (this.buffer.remaining() > maxLen) {
276 final int oldLimit = this.buffer.limit();
277 final int newLimit = oldLimit - (this.buffer.remaining() - maxLen);
278 this.buffer.limit(newLimit);
279 bytesRead = dst.write(this.buffer);
280 this.buffer.limit(oldLimit);
281 } else {
282 bytesRead = dst.write(this.buffer);
283 }
284 return bytesRead;
285 }
286
287 @Override
288 public int read(final WritableByteChannel dst) throws IOException {
289 if (dst == null) {
290 return 0;
291 }
292 setOutputMode();
293 return dst.write(this.buffer);
294 }
295
296 @Override
297 public boolean readLine(
298 final CharArrayBuffer lineBuffer,
299 final boolean endOfStream) throws CharacterCodingException {
300
301 setOutputMode();
302
303 int pos = -1;
304 for (int i = this.buffer.position(); i < this.buffer.limit(); i++) {
305 final int b = this.buffer.get(i);
306 if (b == HTTP.LF) {
307 pos = i + 1;
308 break;
309 }
310 }
311
312 final int maxLineLen = this.constraints.getMaxLineLength();
313 if (maxLineLen > 0) {
314 final int currentLen = (pos > 0 ? pos : this.buffer.limit()) - this.buffer.position();
315 if (currentLen >= maxLineLen) {
316 throw new MessageConstraintException("Maximum line length limit exceeded");
317 }
318 }
319
320 if (pos == -1) {
321 if (endOfStream && this.buffer.hasRemaining()) {
322
323 pos = this.buffer.limit();
324 } else {
325
326
327 return false;
328 }
329 }
330 final int origLimit = this.buffer.limit();
331 this.buffer.limit(pos);
332
333 final int requiredCapacity = this.buffer.limit() - this.buffer.position();
334
335 lineBuffer.ensureCapacity(requiredCapacity);
336
337 if (this.charDecoder == null) {
338 if (this.buffer.hasArray()) {
339 final byte[] b = this.buffer.array();
340 final int off = this.buffer.position();
341 final int len = this.buffer.remaining();
342 lineBuffer.append(b, off, len);
343 this.buffer.position(off + len);
344 } else {
345 while (this.buffer.hasRemaining()) {
346 lineBuffer.append((char) (this.buffer.get() & 0xff));
347 }
348 }
349 } else {
350 if (this.charBuffer == null) {
351 this.charBuffer = CharBuffer.allocate(this.lineBufferSize);
352 }
353 this.charDecoder.reset();
354
355 for (;;) {
356 final CoderResult result = this.charDecoder.decode(
357 this.buffer,
358 this.charBuffer,
359 true);
360 if (result.isError()) {
361 result.throwException();
362 }
363 if (result.isOverflow()) {
364 this.charBuffer.flip();
365 lineBuffer.append(
366 this.charBuffer.array(),
367 this.charBuffer.position(),
368 this.charBuffer.remaining());
369 this.charBuffer.clear();
370 }
371 if (result.isUnderflow()) {
372 break;
373 }
374 }
375
376
377 this.charDecoder.flush(this.charBuffer);
378 this.charBuffer.flip();
379
380 if (this.charBuffer.hasRemaining()) {
381 lineBuffer.append(
382 this.charBuffer.array(),
383 this.charBuffer.position(),
384 this.charBuffer.remaining());
385 }
386
387 }
388 this.buffer.limit(origLimit);
389
390
391 int len = lineBuffer.length();
392 if (len > 0) {
393 if (lineBuffer.charAt(len - 1) == HTTP.LF) {
394 len--;
395 lineBuffer.setLength(len);
396 }
397
398 if (len > 0) {
399 if (lineBuffer.charAt(len - 1) == HTTP.CR) {
400 len--;
401 lineBuffer.setLength(len);
402 }
403 }
404 }
405 return true;
406 }
407
408 @Override
409 public String readLine(final boolean endOfStream) throws CharacterCodingException {
410 final CharArrayBuffer tmpBuffer = new CharArrayBuffer(64);
411 final boolean found = readLine(tmpBuffer, endOfStream);
412 return found ? tmpBuffer.toString() : null;
413 }
414
415 @Override
416 public void clear() {
417 super.clear();
418 }
419
420 }