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.annotation.NotThreadSafe;
42 import org.apache.http.nio.reactor.SessionInputBuffer;
43 import org.apache.http.nio.util.ByteBufferAllocator;
44 import org.apache.http.nio.util.ExpandableBuffer;
45 import org.apache.http.nio.util.HeapByteBufferAllocator;
46 import org.apache.http.params.CoreProtocolPNames;
47 import org.apache.http.params.HttpParams;
48 import org.apache.http.protocol.HTTP;
49 import org.apache.http.util.Args;
50 import org.apache.http.util.CharArrayBuffer;
51 import org.apache.http.util.CharsetUtils;
52
53
54
55
56
57
58
59 @SuppressWarnings("deprecation")
60 @NotThreadSafe
61 public class SessionInputBufferImpl extends ExpandableBuffer implements SessionInputBuffer {
62
63 private final CharsetDecoder chardecoder;
64 private final int lineBuffersize;
65
66 private CharBuffer charbuffer;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public SessionInputBufferImpl(
82 final int buffersize,
83 final int lineBuffersize,
84 final CharsetDecoder chardecoder,
85 final ByteBufferAllocator allocator) {
86 super(buffersize, allocator != null ? allocator : HeapByteBufferAllocator.INSTANCE);
87 this.lineBuffersize = Args.positive(lineBuffersize, "Line buffer size");
88 this.chardecoder = chardecoder;
89 }
90
91
92
93
94
95
96 @Deprecated
97 public SessionInputBufferImpl(
98 final int buffersize,
99 final int lineBuffersize,
100 final ByteBufferAllocator allocator,
101 final HttpParams params) {
102 super(buffersize, allocator);
103 this.lineBuffersize = Args.positive(lineBuffersize, "Line buffer size");
104 final String charsetName = (String) params.getParameter(CoreProtocolPNames.HTTP_ELEMENT_CHARSET);
105 final Charset charset = CharsetUtils.lookup(charsetName);
106 if (charset != null) {
107 this.chardecoder = charset.newDecoder();
108 final CodingErrorAction a1 = (CodingErrorAction) params.getParameter(
109 CoreProtocolPNames.HTTP_MALFORMED_INPUT_ACTION);
110 this.chardecoder.onMalformedInput(a1 != null ? a1 : CodingErrorAction.REPORT);
111 final CodingErrorAction a2 = (CodingErrorAction) params.getParameter(
112 CoreProtocolPNames.HTTP_UNMAPPABLE_INPUT_ACTION);
113 this.chardecoder.onUnmappableCharacter(a2 != null? a2 : CodingErrorAction.REPORT);
114 } else {
115 this.chardecoder = null;
116 }
117 }
118
119
120
121
122
123 @Deprecated
124 public SessionInputBufferImpl(
125 final int buffersize,
126 final int linebuffersize,
127 final HttpParams params) {
128 this(buffersize, linebuffersize, HeapByteBufferAllocator.INSTANCE, params);
129 }
130
131
132
133
134 public SessionInputBufferImpl(
135 final int buffersize,
136 final int lineBuffersize,
137 final Charset charset) {
138 this(buffersize, lineBuffersize,
139 charset != null ? charset.newDecoder() : null, HeapByteBufferAllocator.INSTANCE);
140 }
141
142
143
144
145 public SessionInputBufferImpl(
146 final int buffersize,
147 final int lineBuffersize) {
148 this(buffersize, lineBuffersize, null, HeapByteBufferAllocator.INSTANCE);
149 }
150
151
152
153
154 public SessionInputBufferImpl(final int buffersize) {
155 this(buffersize, 256, null, HeapByteBufferAllocator.INSTANCE);
156 }
157
158 public int fill(final ReadableByteChannel channel) throws IOException {
159 Args.notNull(channel, "Channel");
160 setInputMode();
161 if (!this.buffer.hasRemaining()) {
162 expand();
163 }
164 final int readNo = channel.read(this.buffer);
165 return readNo;
166 }
167
168 public int read() {
169 setOutputMode();
170 return this.buffer.get() & 0xff;
171 }
172
173 public int read(final ByteBuffer dst, final int maxLen) {
174 if (dst == null) {
175 return 0;
176 }
177 setOutputMode();
178 final int len = Math.min(dst.remaining(), maxLen);
179 final int chunk = Math.min(this.buffer.remaining(), len);
180 if (this.buffer.remaining() > chunk) {
181 final int oldLimit = this.buffer.limit();
182 final int newLimit = this.buffer.position() + chunk;
183 this.buffer.limit(newLimit);
184 dst.put(this.buffer);
185 this.buffer.limit(oldLimit);
186 return len;
187 } else {
188 dst.put(this.buffer);
189 }
190 return chunk;
191 }
192
193 public int read(final ByteBuffer dst) {
194 if (dst == null) {
195 return 0;
196 }
197 return read(dst, dst.remaining());
198 }
199
200 public int read(final WritableByteChannel dst, final int maxLen) throws IOException {
201 if (dst == null) {
202 return 0;
203 }
204 setOutputMode();
205 int bytesRead;
206 if (this.buffer.remaining() > maxLen) {
207 final int oldLimit = this.buffer.limit();
208 final int newLimit = oldLimit - (this.buffer.remaining() - maxLen);
209 this.buffer.limit(newLimit);
210 bytesRead = dst.write(this.buffer);
211 this.buffer.limit(oldLimit);
212 } else {
213 bytesRead = dst.write(this.buffer);
214 }
215 return bytesRead;
216 }
217
218 public int read(final WritableByteChannel dst) throws IOException {
219 if (dst == null) {
220 return 0;
221 }
222 setOutputMode();
223 return dst.write(this.buffer);
224 }
225
226 public boolean readLine(
227 final CharArrayBuffer linebuffer,
228 final boolean endOfStream) throws CharacterCodingException {
229
230 setOutputMode();
231
232 int pos = -1;
233 boolean hasLine = false;
234 for (int i = this.buffer.position(); i < this.buffer.limit(); i++) {
235 final int b = this.buffer.get(i);
236 if (b == HTTP.LF) {
237 hasLine = true;
238 pos = i + 1;
239 break;
240 }
241 }
242 if (!hasLine) {
243 if (endOfStream && this.buffer.hasRemaining()) {
244
245 pos = this.buffer.limit();
246 } else {
247
248
249 return false;
250 }
251 }
252 final int origLimit = this.buffer.limit();
253 this.buffer.limit(pos);
254
255 final int requiredCapacity = this.buffer.limit() - this.buffer.position();
256
257 linebuffer.ensureCapacity(requiredCapacity);
258
259 if (this.chardecoder == null) {
260 if (this.buffer.hasArray()) {
261 final byte[] b = this.buffer.array();
262 final int off = this.buffer.position();
263 final int len = this.buffer.remaining();
264 linebuffer.append(b, off, len);
265 this.buffer.position(off + len);
266 } else {
267 while (this.buffer.hasRemaining()) {
268 linebuffer.append((char) (this.buffer.get() & 0xff));
269 }
270 }
271 } else {
272 if (this.charbuffer == null) {
273 this.charbuffer = CharBuffer.allocate(this.lineBuffersize);
274 }
275 this.chardecoder.reset();
276
277 for (;;) {
278 final CoderResult result = this.chardecoder.decode(
279 this.buffer,
280 this.charbuffer,
281 true);
282 if (result.isError()) {
283 result.throwException();
284 }
285 if (result.isOverflow()) {
286 this.charbuffer.flip();
287 linebuffer.append(
288 this.charbuffer.array(),
289 this.charbuffer.position(),
290 this.charbuffer.remaining());
291 this.charbuffer.clear();
292 }
293 if (result.isUnderflow()) {
294 break;
295 }
296 }
297
298
299 this.chardecoder.flush(this.charbuffer);
300 this.charbuffer.flip();
301
302 if (this.charbuffer.hasRemaining()) {
303 linebuffer.append(
304 this.charbuffer.array(),
305 this.charbuffer.position(),
306 this.charbuffer.remaining());
307 }
308
309 }
310 this.buffer.limit(origLimit);
311
312
313 int l = linebuffer.length();
314 if (l > 0) {
315 if (linebuffer.charAt(l - 1) == HTTP.LF) {
316 l--;
317 linebuffer.setLength(l);
318 }
319
320 if (l > 0) {
321 if (linebuffer.charAt(l - 1) == HTTP.CR) {
322 l--;
323 linebuffer.setLength(l);
324 }
325 }
326 }
327 return true;
328 }
329
330 public String readLine(final boolean endOfStream) throws CharacterCodingException {
331 final CharArrayBuffer charbuffer = new CharArrayBuffer(64);
332 final boolean found = readLine(charbuffer, endOfStream);
333 if (found) {
334 return charbuffer.toString();
335 } else {
336 return null;
337 }
338 }
339
340 }