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