1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

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 /**
47  * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3
48  * OutputBuffer, adapted to handle input instead of output. This allows
49  * complete recycling of the facade objects (the ServletInputStream and the
50  * BufferedReader).
51  *
52  * @author Remy Maucherat
53  */

54 public class InputBuffer extends Reader
55     implements ByteChunk.ByteInputChannel, ApplicationBufferHandler {
56
57     /**
58      * The string manager for this package.
59      */

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     // The buffer can be used for byte[] and char[] reading
67     // ( this is needed to support ServletInputStream and BufferedReader )
68     public final int INITIAL_STATE = 0;
69     public final int CHAR_STATE = 1;
70     public final int BYTE_STATE = 2;
71
72
73     /**
74      * Encoder cache.
75      */

76     private static final Map<Charset, SynchronizedStack<B2CConverter>> encoders = new ConcurrentHashMap<>();
77
78     // ----------------------------------------------------- Instance Variables
79
80     /**
81      * The byte buffer.
82      */

83     private ByteBuffer bb;
84
85
86     /**
87      * The char buffer.
88      */

89     private CharBuffer cb;
90
91
92     /**
93      * State of the output buffer.
94      */

95     private int state = 0;
96
97
98     /**
99      * Flag which indicates if the input buffer is closed.
100      */

101     private boolean closed = false;
102
103
104     /**
105      * Current byte to char converter.
106      */

107     protected B2CConverter conv;
108
109
110     /**
111      * Associated Coyote request.
112      */

113     private Request coyoteRequest;
114
115
116     /**
117      * Buffer position.
118      */

119     private int markPos = -1;
120
121
122     /**
123      * Char buffer limit.
124      */

125     private int readLimit;
126
127
128     /**
129      * Buffer size.
130      */

131     private final int size;
132
133
134     // ----------------------------------------------------------- Constructors
135
136
137     /**
138      * Default constructor. Allocate the buffer with the default buffer size.
139      */

140     public InputBuffer() {
141
142         this(DEFAULT_BUFFER_SIZE);
143
144     }
145
146
147     /**
148      * Alternate constructor which allows specifying the initial buffer size.
149      *
150      * @param size Buffer size to use
151      */

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     // ------------------------------------------------------------- Properties
165
166
167     /**
168      * Associated Coyote request.
169      *
170      * @param coyoteRequest Associated Coyote request
171      */

172     public void setRequest(Request coyoteRequest) {
173         this.coyoteRequest = coyoteRequest;
174     }
175
176
177     // --------------------------------------------------------- Public Methods
178
179     /**
180      * Recycle the output buffer.
181      */

182     public void recycle() {
183
184         state = INITIAL_STATE;
185
186         // If usage of mark made the buffer too big, reallocate it
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     /**
207      * Close the input buffer.
208      *
209      * @throws IOException An underlying IOException occurred
210      */

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         // The container is responsible for the first call to
243         // listener.onDataAvailable(). If isReady() returns true, the container
244         // needs to call listener.onDataAvailable() from a new thread. If
245         // isReady() returns false, the socket will be registered for read and
246         // the container will call listener.onDataAvailable() once data arrives.
247         // Must call isFinished() first as a call to isReady() if the request
248         // has been finished will register the socket for read interest and that
249         // is not required.
250         if (!coyoteRequest.isFinished() && isReady()) {
251             coyoteRequest.action(ActionCode.DISPATCH_READ, null);
252             if (!ContainerThreadMarker.isContainerThread()) {
253                 // Not on a container thread so need to execute the dispatch
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             // If this is a non-container thread, need to trigger a read
284             // which will eventually lead to a call to onAllDataRead() via a
285             // container thread.
286             if (!ContainerThreadMarker.isContainerThread()) {
287                 coyoteRequest.action(ActionCode.DISPATCH_READ, null);
288                 coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null);
289             }
290             return false;
291         }
292         // Checking for available data at the network level and registering for
293         // read can be done sequentially for HTTP/1.x and AJP as there is only
294         // ever a single thread processing the socket at any one time. However,
295         // for HTTP/2 there is one thread processing the connection and separate
296         // threads for each stream. For HTTP/2 the two operations have to be
297         // performed atomically else it is possible for the connection thread to
298         // read more data in to the buffer after the stream thread checks for
299         // available network data but before it registers for read.
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     // ------------------------------------------------- Bytes Handling Methods
316
317     /**
318      * Reads new bytes in the byte chunk.
319      *
320      * @throws IOException An underlying IOException occurred
321      */

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             // An IOException on a read is almost always due to
339             // the remote client aborting the request.
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     /**
372      * Transfers bytes from the buffer to the specified ByteBuffer. After the
373      * operation the position of the ByteBuffer will be returned to the one
374      * before the operation, the limit will be the position incremented by
375      * the number of the transferred bytes.
376      *
377      * @param to the ByteBuffer into which bytes are to be written.
378      * @return an integer specifying the actual number of bytes read, or -1 if
379      *         the end of the stream is reached
380      * @throws IOException if an input or output exception has occurred
381      */

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     // ------------------------------------------------- Chars Handling Methods
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             // Make sure there's enough space in the worst case
418             makeSpace(bb.remaining());
419             if ((cb.capacity() - cb.limit()) == 0 && bb.remaining() != 0) {
420                 // We went over the limit
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         // no-op
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