1
17 package org.apache.catalina.connector;
18
19 import java.io.IOException;
20 import java.io.Reader;
21 import java.nio.Buffer;
22 import java.nio.ByteBuffer;
23 import java.nio.CharBuffer;
24 import java.nio.charset.Charset;
25 import java.security.AccessController;
26 import java.security.PrivilegedActionException;
27 import java.security.PrivilegedExceptionAction;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.atomic.AtomicBoolean;
31
32 import javax.servlet.ReadListener;
33
34 import org.apache.catalina.security.SecurityUtil;
35 import org.apache.coyote.ActionCode;
36 import org.apache.coyote.ContainerThreadMarker;
37 import org.apache.coyote.Request;
38 import org.apache.juli.logging.Log;
39 import org.apache.juli.logging.LogFactory;
40 import org.apache.tomcat.util.buf.B2CConverter;
41 import org.apache.tomcat.util.buf.ByteChunk;
42 import org.apache.tomcat.util.collections.SynchronizedStack;
43 import org.apache.tomcat.util.net.ApplicationBufferHandler;
44 import org.apache.tomcat.util.res.StringManager;
45
46
54 public class InputBuffer extends Reader
55 implements ByteChunk.ByteInputChannel, ApplicationBufferHandler {
56
57
60 protected static final StringManager sm = StringManager.getManager(InputBuffer.class);
61
62 private static final Log log = LogFactory.getLog(InputBuffer.class);
63
64 public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
65
66
67
68 public final int INITIAL_STATE = 0;
69 public final int CHAR_STATE = 1;
70 public final int BYTE_STATE = 2;
71
72
73
76 private static final Map<Charset, SynchronizedStack<B2CConverter>> encoders = new ConcurrentHashMap<>();
77
78
79
80
83 private ByteBuffer bb;
84
85
86
89 private CharBuffer cb;
90
91
92
95 private int state = 0;
96
97
98
101 private boolean closed = false;
102
103
104
107 protected B2CConverter conv;
108
109
110
113 private Request coyoteRequest;
114
115
116
119 private int markPos = -1;
120
121
122
125 private int readLimit;
126
127
128
131 private final int size;
132
133
134
135
136
137
140 public InputBuffer() {
141
142 this(DEFAULT_BUFFER_SIZE);
143
144 }
145
146
147
152 public InputBuffer(int size) {
153
154 this.size = size;
155 bb = ByteBuffer.allocate(size);
156 clear(bb);
157 cb = CharBuffer.allocate(size);
158 clear(cb);
159 readLimit = size;
160
161 }
162
163
164
165
166
167
172 public void setRequest(Request coyoteRequest) {
173 this.coyoteRequest = coyoteRequest;
174 }
175
176
177
178
179
182 public void recycle() {
183
184 state = INITIAL_STATE;
185
186
187 if (cb.capacity() > size) {
188 cb = CharBuffer.allocate(size);
189 clear(cb);
190 } else {
191 clear(cb);
192 }
193 readLimit = size;
194 markPos = -1;
195 clear(bb);
196 closed = false;
197
198 if (conv != null) {
199 conv.recycle();
200 encoders.get(conv.getCharset()).push(conv);
201 conv = null;
202 }
203 }
204
205
206
211 @Override
212 public void close() throws IOException {
213 closed = true;
214 }
215
216
217 public int available() {
218 int available = availableInThisBuffer();
219 if (available == 0) {
220 coyoteRequest.action(ActionCode.AVAILABLE,
221 Boolean.valueOf(coyoteRequest.getReadListener() != null));
222 available = (coyoteRequest.getAvailable() > 0) ? 1 : 0;
223 }
224 return available;
225 }
226
227
228 private int availableInThisBuffer() {
229 int available = 0;
230 if (state == BYTE_STATE) {
231 available = bb.remaining();
232 } else if (state == CHAR_STATE) {
233 available = cb.remaining();
234 }
235 return available;
236 }
237
238
239 public void setReadListener(ReadListener listener) {
240 coyoteRequest.setReadListener(listener);
241
242
243
244
245
246
247
248
249
250 if (!coyoteRequest.isFinished() && isReady()) {
251 coyoteRequest.action(ActionCode.DISPATCH_READ, null);
252 if (!ContainerThreadMarker.isContainerThread()) {
253
254 coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null);
255 }
256 }
257 }
258
259
260 public boolean isFinished() {
261 int available = 0;
262 if (state == BYTE_STATE) {
263 available = bb.remaining();
264 } else if (state == CHAR_STATE) {
265 available = cb.remaining();
266 }
267 if (available > 0) {
268 return false;
269 } else {
270 return coyoteRequest.isFinished();
271 }
272 }
273
274
275 public boolean isReady() {
276 if (coyoteRequest.getReadListener() == null) {
277 if (log.isDebugEnabled()) {
278 log.debug(sm.getString("inputBuffer.requiresNonBlocking"));
279 }
280 return false;
281 }
282 if (isFinished()) {
283
284
285
286 if (!ContainerThreadMarker.isContainerThread()) {
287 coyoteRequest.action(ActionCode.DISPATCH_READ, null);
288 coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null);
289 }
290 return false;
291 }
292
293
294
295
296
297
298
299
300 if (availableInThisBuffer() > 0) {
301 return true;
302 }
303
304 AtomicBoolean result = new AtomicBoolean();
305 coyoteRequest.action(ActionCode.NB_READ_INTEREST, result);
306 return result.get();
307 }
308
309
310 boolean isBlocking() {
311 return coyoteRequest.getReadListener() == null;
312 }
313
314
315
316
317
322 @Override
323 public int realReadBytes() throws IOException {
324 if (closed) {
325 return -1;
326 }
327 if (coyoteRequest == null) {
328 return -1;
329 }
330
331 if (state == INITIAL_STATE) {
332 state = BYTE_STATE;
333 }
334
335 try {
336 return coyoteRequest.doRead(this);
337 } catch (IOException ioe) {
338
339
340 throw new ClientAbortException(ioe);
341 }
342 }
343
344
345 public int readByte() throws IOException {
346 if (closed) {
347 throw new IOException(sm.getString("inputBuffer.streamClosed"));
348 }
349
350 if (checkByteBufferEof()) {
351 return -1;
352 }
353 return bb.get() & 0xFF;
354 }
355
356
357 public int read(byte[] b, int off, int len) throws IOException {
358 if (closed) {
359 throw new IOException(sm.getString("inputBuffer.streamClosed"));
360 }
361
362 if (checkByteBufferEof()) {
363 return -1;
364 }
365 int n = Math.min(len, bb.remaining());
366 bb.get(b, off, n);
367 return n;
368 }
369
370
371
382 public int read(ByteBuffer to) throws IOException {
383 if (closed) {
384 throw new IOException(sm.getString("inputBuffer.streamClosed"));
385 }
386
387 if (checkByteBufferEof()) {
388 return -1;
389 }
390 int n = Math.min(to.remaining(), bb.remaining());
391 int orgLimit = bb.limit();
392 bb.limit(bb.position() + n);
393 to.put(bb);
394 bb.limit(orgLimit);
395 to.limit(to.position()).position(to.position() - n);
396 return n;
397 }
398
399
400
401
402 public int realReadChars() throws IOException {
403 checkConverter();
404
405 boolean eof = false;
406
407 if (bb.remaining() <= 0) {
408 int nRead = realReadBytes();
409 if (nRead < 0) {
410 eof = true;
411 }
412 }
413
414 if (markPos == -1) {
415 clear(cb);
416 } else {
417
418 makeSpace(bb.remaining());
419 if ((cb.capacity() - cb.limit()) == 0 && bb.remaining() != 0) {
420
421 clear(cb);
422 markPos = -1;
423 }
424 }
425
426 state = CHAR_STATE;
427 conv.convert(bb, cb, this, eof);
428
429 if (cb.remaining() == 0 && eof) {
430 return -1;
431 } else {
432 return cb.remaining();
433 }
434 }
435
436
437 @Override
438 public int read() throws IOException {
439
440 if (closed) {
441 throw new IOException(sm.getString("inputBuffer.streamClosed"));
442 }
443
444 if (checkCharBufferEof()) {
445 return -1;
446 }
447 return cb.get();
448 }
449
450
451 @Override
452 public int read(char[] cbuf) throws IOException {
453
454 if (closed) {
455 throw new IOException(sm.getString("inputBuffer.streamClosed"));
456 }
457
458 return read(cbuf, 0, cbuf.length);
459 }
460
461
462 @Override
463 public int read(char[] cbuf, int off, int len) throws IOException {
464
465 if (closed) {
466 throw new IOException(sm.getString("inputBuffer.streamClosed"));
467 }
468
469 if (checkCharBufferEof()) {
470 return -1;
471 }
472 int n = Math.min(len, cb.remaining());
473 cb.get(cbuf, off, n);
474 return n;
475 }
476
477
478 @Override
479 public long skip(long n) throws IOException {
480 if (closed) {
481 throw new IOException(sm.getString("inputBuffer.streamClosed"));
482 }
483
484 if (n < 0) {
485 throw new IllegalArgumentException();
486 }
487
488 long nRead = 0;
489 while (nRead < n) {
490 if (cb.remaining() >= n) {
491 cb.position(cb.position() + (int) n);
492 nRead = n;
493 } else {
494 nRead += cb.remaining();
495 cb.position(cb.limit());
496 int nb = realReadChars();
497 if (nb < 0) {
498 break;
499 }
500 }
501 }
502 return nRead;
503 }
504
505
506 @Override
507 public boolean ready() throws IOException {
508 if (closed) {
509 throw new IOException(sm.getString("inputBuffer.streamClosed"));
510 }
511 if (state == INITIAL_STATE) {
512 state = CHAR_STATE;
513 }
514 return (available() > 0);
515 }
516
517
518 @Override
519 public boolean markSupported() {
520 return true;
521 }
522
523
524 @Override
525 public void mark(int readAheadLimit) throws IOException {
526
527 if (closed) {
528 throw new IOException(sm.getString("inputBuffer.streamClosed"));
529 }
530
531 if (cb.remaining() <= 0) {
532 clear(cb);
533 } else {
534 if ((cb.capacity() > (2 * size)) && (cb.remaining()) < (cb.position())) {
535 cb.compact();
536 cb.flip();
537 }
538 }
539 readLimit = cb.position() + readAheadLimit + size;
540 markPos = cb.position();
541 }
542
543
544 @Override
545 public void reset() throws IOException {
546
547 if (closed) {
548 throw new IOException(sm.getString("inputBuffer.streamClosed"));
549 }
550
551 if (state == CHAR_STATE) {
552 if (markPos < 0) {
553 clear(cb);
554 markPos = -1;
555 throw new IOException();
556 } else {
557 cb.position(markPos);
558 }
559 } else {
560 clear(bb);
561 }
562 }
563
564
565 public void checkConverter() throws IOException {
566 if (conv != null) {
567 return;
568 }
569
570 Charset charset = null;
571 if (coyoteRequest != null) {
572 charset = coyoteRequest.getCharset();
573 }
574
575 if (charset == null) {
576 charset = org.apache.coyote.Constants.DEFAULT_BODY_CHARSET;
577 }
578
579 SynchronizedStack<B2CConverter> stack = encoders.get(charset);
580 if (stack == null) {
581 stack = new SynchronizedStack<>();
582 encoders.putIfAbsent(charset, stack);
583 stack = encoders.get(charset);
584 }
585 conv = stack.pop();
586
587 if (conv == null) {
588 conv = createConverter(charset);
589 }
590 }
591
592
593 private static B2CConverter createConverter(Charset charset) throws IOException {
594 if (SecurityUtil.isPackageProtectionEnabled()) {
595 try {
596 return AccessController.doPrivileged(new PrivilegedCreateConverter(charset));
597 } catch (PrivilegedActionException ex) {
598 Exception e = ex.getException();
599 if (e instanceof IOException) {
600 throw (IOException) e;
601 } else {
602 throw new IOException(e);
603 }
604 }
605 } else {
606 return new B2CConverter(charset);
607 }
608
609 }
610
611
612 @Override
613 public void setByteBuffer(ByteBuffer buffer) {
614 bb = buffer;
615 }
616
617
618 @Override
619 public ByteBuffer getByteBuffer() {
620 return bb;
621 }
622
623
624 @Override
625 public void expand(int size) {
626
627 }
628
629
630 private boolean checkByteBufferEof() throws IOException {
631 if (bb.remaining() == 0) {
632 int n = realReadBytes();
633 if (n < 0) {
634 return true;
635 }
636 }
637 return false;
638 }
639
640 private boolean checkCharBufferEof() throws IOException {
641 if (cb.remaining() == 0) {
642 int n = realReadChars();
643 if (n < 0) {
644 return true;
645 }
646 }
647 return false;
648 }
649
650 private void clear(Buffer buffer) {
651 buffer.rewind().limit(0);
652 }
653
654 private void makeSpace(int count) {
655 int desiredSize = cb.limit() + count;
656 if(desiredSize > readLimit) {
657 desiredSize = readLimit;
658 }
659
660 if(desiredSize <= cb.capacity()) {
661 return;
662 }
663
664 int newSize = 2 * cb.capacity();
665 if(desiredSize >= newSize) {
666 newSize= 2 * cb.capacity() + count;
667 }
668
669 if (newSize > readLimit) {
670 newSize = readLimit;
671 }
672
673 CharBuffer tmp = CharBuffer.allocate(newSize);
674 int oldPosition = cb.position();
675 cb.position(0);
676 tmp.put(cb);
677 tmp.flip();
678 tmp.position(oldPosition);
679 cb = tmp;
680 tmp = null;
681 }
682
683
684 private static class PrivilegedCreateConverter
685 implements PrivilegedExceptionAction<B2CConverter> {
686
687 private final Charset charset;
688
689 public PrivilegedCreateConverter(Charset charset) {
690 this.charset = charset;
691 }
692
693 @Override
694 public B2CConverter run() throws IOException {
695 return new B2CConverter(charset);
696 }
697 }
698 }
699