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.ByteBuffer;
22 import java.nio.charset.StandardCharsets;
23
24 import org.apache.coyote.InputBuffer;
25 import org.apache.coyote.Request;
26 import org.apache.coyote.http11.InputFilter;
27 import org.apache.tomcat.util.buf.ByteChunk;
28 import org.apache.tomcat.util.net.ApplicationBufferHandler;
29 import org.apache.tomcat.util.res.StringManager;
30
31 /**
32  * Identity input filter.
33  *
34  * @author Remy Maucherat
35  */

36 public class IdentityInputFilter implements InputFilter, ApplicationBufferHandler {
37
38     private static final StringManager sm = StringManager.getManager(
39             IdentityInputFilter.class.getPackage().getName());
40
41
42     // -------------------------------------------------------------- Constants
43
44
45     protected static final String ENCODING_NAME = "identity";
46     protected static final ByteChunk ENCODING = new ByteChunk();
47
48
49     // ----------------------------------------------------- Static Initializer
50
51
52     static {
53         ENCODING.setBytes(ENCODING_NAME.getBytes(StandardCharsets.ISO_8859_1),
54                 0, ENCODING_NAME.length());
55     }
56
57
58     // ----------------------------------------------------- Instance Variables
59
60
61     /**
62      * Content length.
63      */

64     protected long contentLength = -1;
65
66
67     /**
68      * Remaining bytes.
69      */

70     protected long remaining = 0;
71
72
73     /**
74      * Next buffer in the pipeline.
75      */

76     protected InputBuffer buffer;
77
78
79     /**
80      * ByteBuffer used to read leftover bytes.
81      */

82     protected ByteBuffer tempRead;
83
84
85     private final int maxSwallowSize;
86
87
88     public IdentityInputFilter(int maxSwallowSize) {
89         this.maxSwallowSize = maxSwallowSize;
90     }
91
92
93     // ---------------------------------------------------- InputBuffer Methods
94
95     @Override
96     public int doRead(ApplicationBufferHandler handler) throws IOException {
97
98         int result = -1;
99
100         if (contentLength >= 0) {
101             if (remaining > 0) {
102                 int nRead = buffer.doRead(handler);
103                 if (nRead > remaining) {
104                     // The chunk is longer than the number of bytes remaining
105                     // in the body; changing the chunk length to the number
106                     // of bytes remaining
107                     handler.getByteBuffer().limit(handler.getByteBuffer().position() + (int) remaining);
108                     result = (int) remaining;
109                 } else {
110                     result = nRead;
111                 }
112                 if (nRead > 0) {
113                     remaining = remaining - nRead;
114                 }
115             } else {
116                 // No more bytes left to be read : return -1 and clear the
117                 // buffer
118                 if (handler.getByteBuffer() != null) {
119                     handler.getByteBuffer().position(0).limit(0);
120                 }
121                 result = -1;
122             }
123         }
124
125         return result;
126
127     }
128
129
130     // ---------------------------------------------------- InputFilter Methods
131
132
133     /**
134      * Read the content length from the request.
135      */

136     @Override
137     public void setRequest(Request request) {
138         contentLength = request.getContentLengthLong();
139         remaining = contentLength;
140     }
141
142
143     @Override
144     public long end() throws IOException {
145
146         final boolean maxSwallowSizeExceeded = (maxSwallowSize > -1 && remaining > maxSwallowSize);
147         long swallowed = 0;
148
149         // Consume extra bytes.
150         while (remaining > 0) {
151
152             int nread = buffer.doRead(this);
153             tempRead = null;
154             if (nread > 0 ) {
155                 swallowed += nread;
156                 remaining = remaining - nread;
157                 if (maxSwallowSizeExceeded && swallowed > maxSwallowSize) {
158                     // Note: We do not fail early so the client has a chance to
159                     // read the response before the connection is closed. See:
160                     // https://httpd.apache.org/docs/2.0/misc/fin_wait_2.html#appendix
161                     throw new IOException(sm.getString("inputFilter.maxSwallow"));
162                 }
163             } else { // errors are handled higher up.
164                 remaining = 0;
165             }
166         }
167
168         // If too many bytes were read, return the amount.
169         return -remaining;
170
171     }
172
173
174     /**
175      * Amount of bytes still available in a buffer.
176      */

177     @Override
178     public int available() {
179         return 0;
180     }
181
182
183     /**
184      * Set the next buffer in the filter pipeline.
185      */

186     @Override
187     public void setBuffer(InputBuffer buffer) {
188         this.buffer = buffer;
189     }
190
191
192     /**
193      * Make the filter ready to process the next request.
194      */

195     @Override
196     public void recycle() {
197         contentLength = -1;
198         remaining = 0;
199     }
200
201
202     /**
203      * Return the name of the associated encoding; Here, the value is
204      * "identity".
205      */

206     @Override
207     public ByteChunk getEncodingName() {
208         return ENCODING;
209     }
210
211
212     @Override
213     public boolean isFinished() {
214         // Only finished if a content length is defined and there is no data
215         // remaining
216         return contentLength > -1 && remaining <= 0;
217     }
218
219
220     @Override
221     public void setByteBuffer(ByteBuffer buffer) {
222         tempRead = buffer;
223     }
224
225
226     @Override
227     public ByteBuffer getByteBuffer() {
228         return tempRead;
229     }
230
231
232     @Override
233     public void expand(int size) {
234         // no-op
235     }
236 }
237