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
18 package org.apache.coyote.http11.filters;
19
20 import java.io.IOException;
21 import java.nio.BufferOverflowException;
22 import java.nio.ByteBuffer;
23 import java.nio.charset.StandardCharsets;
24
25 import org.apache.coyote.InputBuffer;
26 import org.apache.coyote.Request;
27 import org.apache.coyote.http11.InputFilter;
28 import org.apache.tomcat.util.buf.ByteChunk;
29 import org.apache.tomcat.util.net.ApplicationBufferHandler;
30
31 /**
32  * Input filter responsible for reading and buffering the request body, so that
33  * it does not interfere with client SSL handshake messages.
34  */

35 public class BufferedInputFilter implements InputFilter, ApplicationBufferHandler {
36
37     // -------------------------------------------------------------- Constants
38
39     private static final String ENCODING_NAME = "buffered";
40     private static final ByteChunk ENCODING = new ByteChunk();
41
42
43     // ----------------------------------------------------- Instance Variables
44
45     private ByteBuffer buffered;
46     private ByteBuffer tempRead;
47     private InputBuffer buffer;
48     private boolean hasRead = false;
49
50
51     // ----------------------------------------------------- Static Initializer
52
53     static {
54         ENCODING.setBytes(ENCODING_NAME.getBytes(StandardCharsets.ISO_8859_1),
55                 0, ENCODING_NAME.length());
56     }
57
58
59     // --------------------------------------------------------- Public Methods
60
61
62     /**
63      * Set the buffering limit. This should be reset every time the buffer is
64      * used.
65      *
66      * @param limit The maximum number of bytes that will be buffered
67      */

68     public void setLimit(int limit) {
69         if (buffered == null) {
70             buffered = ByteBuffer.allocate(limit);
71             buffered.flip();
72         }
73     }
74
75
76     // ---------------------------------------------------- InputBuffer Methods
77
78
79     /**
80      * Reads the request body and buffers it.
81      */

82     @Override
83     public void setRequest(Request request) {
84         // save off the Request body
85         try {
86             while (buffer.doRead(this) >= 0) {
87                 buffered.mark().position(buffered.limit()).limit(buffered.capacity());
88                 buffered.put(tempRead);
89                 buffered.limit(buffered.position()).reset();
90                 tempRead = null;
91             }
92         } catch(IOException | BufferOverflowException ioe) {
93             // No need for i18n - this isn't going to get logged anywhere
94             throw new IllegalStateException(
95                     "Request body too large for buffer");
96         }
97     }
98
99     /**
100      * Fills the given ByteBuffer with the buffered request body.
101      */

102     @Override
103     public int doRead(ApplicationBufferHandler handler) throws IOException {
104         if (isFinished()) {
105             return -1;
106         }
107
108         handler.setByteBuffer(buffered);
109         hasRead = true;
110         return buffered.remaining();
111     }
112
113     @Override
114     public void setBuffer(InputBuffer buffer) {
115         this.buffer = buffer;
116     }
117
118     @Override
119     public void recycle() {
120         if (buffered != null) {
121             if (buffered.capacity() > 65536) {
122                 buffered = null;
123             } else {
124                 buffered.position(0).limit(0);
125             }
126         }
127         hasRead = false;
128         buffer = null;
129     }
130
131     @Override
132     public ByteChunk getEncodingName() {
133         return ENCODING;
134     }
135
136     @Override
137     public long end() throws IOException {
138         return 0;
139     }
140
141     @Override
142     public int available() {
143         return buffered.remaining();
144     }
145
146
147     @Override
148     public boolean isFinished() {
149         return hasRead || buffered.remaining() <= 0;
150     }
151
152
153     @Override
154     public void setByteBuffer(ByteBuffer buffer) {
155         tempRead = buffer;
156     }
157
158
159     @Override
160     public ByteBuffer getByteBuffer() {
161         return tempRead;
162     }
163
164
165     @Override
166     public void expand(int size) {
167         // no-op
168     }
169 }
170