View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.mina.http;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertNotEquals;
24  import static org.junit.Assert.assertTrue;
25  
26  import java.nio.charset.CharacterCodingException;
27  import java.nio.charset.Charset;
28  import java.nio.charset.CharsetEncoder;
29  
30  import org.apache.mina.core.buffer.IoBuffer;
31  import org.apache.mina.core.filterchain.IoFilter.NextFilter;
32  import org.apache.mina.core.session.DummySession;
33  import org.apache.mina.core.session.IoSession;
34  import org.apache.mina.filter.codec.AbstractProtocolDecoderOutput;
35  import org.apache.mina.filter.codec.ProtocolDecoder;
36  import org.apache.mina.http.api.HttpEndOfContent;
37  import org.apache.mina.http.api.HttpRequest;
38  import org.junit.Test;
39  
40  public class HttpServerDecoderTest {
41      private static final CharsetEncoder encoder = Charset.forName("US-ASCII").newEncoder(); //$NON-NLS-1$
42  
43      private static final ProtocolDecoder decoder = new HttpServerDecoder();
44  
45      private static final String DECODER_STATE_ATT = "http.ds";
46      
47      /*
48       * Use a single session for all requests in order to test state management better
49       */
50      private static IoSession session = new DummySession();
51  
52      /**
53       * Build an IO buffer containing a simple minimal HTTP request.
54       * 
55       * @param method the HTTP method
56       * @param body the option body
57       * @return the built IO buffer
58       * @throws CharacterCodingException if encoding fails
59       */
60      protected static IoBuffer getRequestBuffer(String method, String body) throws CharacterCodingException {
61          IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
62          buffer.putString(method + " / HTTP/1.1\r\nHost: dummy\r\n", encoder);
63          
64          if (body != null) {
65              buffer.putString("Content-Length: " + body.length() + "\r\n\r\n", encoder);
66              buffer.putString(body, encoder);
67          } else {
68              buffer.putString("\r\n", encoder);
69          }
70          
71          buffer.rewind();
72          
73          return buffer;
74      }
75  
76      protected static IoBuffer getRequestBuffer(String method) throws CharacterCodingException {
77          return getRequestBuffer(method, null);
78      }
79  
80      /**
81       * Execute an HTPP request and return the queue of messages.
82       * 
83       * @param method the HTTP method
84       * @param body the optional body
85       * @return the protocol output and its queue of messages
86       * @throws Exception if error occurs (encoding,...)
87       */
88      protected static AbstractProtocolDecoderOutput executeRequest(String method, String body) throws Exception {
89          AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
90              public void flush(NextFilter nextFilter, IoSession session) {
91              }
92          };
93  
94          IoBuffer buffer = getRequestBuffer(method, body); //$NON-NLS-1$
95          
96          while (buffer.hasRemaining()) {
97              decoder.decode(session, buffer, out);
98          }
99          
100         return out;
101     }
102 
103     @Test
104     public void testGetRequestWithoutBody() throws Exception {
105         AbstractProtocolDecoderOutput out = executeRequest("GET", null);
106         assertEquals(2, out.getMessageQueue().size());
107         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
108         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
109     }
110 
111     @Test
112     public void testGetRequestBody() throws Exception {
113         AbstractProtocolDecoderOutput out = executeRequest("GET", "body");
114         assertEquals(3, out.getMessageQueue().size());
115         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
116         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
117         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
118     }
119 
120     @Test
121     public void testPutRequestWithoutBody() throws Exception {
122         AbstractProtocolDecoderOutput out = executeRequest("PUT", null);
123         assertEquals(2, out.getMessageQueue().size());
124         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
125         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
126     }
127 
128     @Test
129     public void testPutRequestBody() throws Exception {
130         AbstractProtocolDecoderOutput out = executeRequest("PUT", "body");
131         assertEquals(3, out.getMessageQueue().size());
132         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
133         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
134         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
135     }
136 
137     @Test
138     public void testPostRequestWithoutBody() throws Exception {
139         AbstractProtocolDecoderOutput out = executeRequest("POST", null);
140         assertEquals(2, out.getMessageQueue().size());
141         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
142         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
143     }
144 
145     @Test
146     public void testPostRequestBody() throws Exception {
147         AbstractProtocolDecoderOutput out = executeRequest("POST", "body");
148         assertEquals(3, out.getMessageQueue().size());
149         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
150         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
151         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
152     }
153 
154     @Test
155     public void testDeleteRequestWithoutBody() throws Exception {
156         AbstractProtocolDecoderOutput out = executeRequest("DELETE", null);
157         assertEquals(2, out.getMessageQueue().size());
158         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
159         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
160     }
161 
162     @Test
163     public void testDeleteRequestBody() throws Exception {
164         AbstractProtocolDecoderOutput out = executeRequest("DELETE", "body");
165         assertEquals(3, out.getMessageQueue().size());
166         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
167         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
168         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
169     }
170     
171     @Test
172     public void testDIRMINA965NoContent() throws Exception {
173         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
174             public void flush(NextFilter nextFilter, IoSession session) {
175             }
176         };
177         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
178         buffer.putString("GET / HTTP/1.1\r\nHost: ", encoder);
179         buffer.rewind();
180         while (buffer.hasRemaining()) {
181             decoder.decode(session, buffer, out);
182         }
183         buffer = IoBuffer.allocate(0).setAutoExpand(true);
184         buffer.putString("dummy\r\n\r\n", encoder);
185         buffer.rewind();
186         while (buffer.hasRemaining()) {
187             decoder.decode(session, buffer, out);
188         }
189         assertEquals(2, out.getMessageQueue().size());
190         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
191         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
192     }
193 
194     @Test
195     public void testDIRMINA965WithContent() throws Exception {
196         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
197             public void flush(NextFilter nextFilter, IoSession session) {
198             }
199         };
200         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
201         buffer.putString("GET / HTTP/1.1\r\nHost: ", encoder);
202         buffer.rewind();
203         while (buffer.hasRemaining()) {
204             decoder.decode(session, buffer, out);
205         }
206         buffer = IoBuffer.allocate(0).setAutoExpand(true);
207         buffer.putString("dummy\r\nContent-Length: 1\r\n\r\nA", encoder);
208         buffer.rewind();
209         while (buffer.hasRemaining()) {
210             decoder.decode(session, buffer, out);
211         }
212         assertEquals(3, out.getMessageQueue().size());
213         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
214         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
215         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
216     }
217     @Test
218     public void testDIRMINA965WithContentOnTwoChunks() throws Exception {
219         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
220             public void flush(NextFilter nextFilter, IoSession session) {
221             }
222         };
223         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
224         buffer.putString("GET / HTTP/1.1\r\nHost: ", encoder);
225         buffer.rewind();
226         while (buffer.hasRemaining()) {
227             decoder.decode(session, buffer, out);
228         }
229         buffer = IoBuffer.allocate(0).setAutoExpand(true);
230         buffer.putString("dummy\r\nContent-Length: 2\r\n\r\nA", encoder);
231         buffer.rewind();
232         while (buffer.hasRemaining()) {
233             decoder.decode(session, buffer, out);
234         }
235         buffer = IoBuffer.allocate(0).setAutoExpand(true);
236         buffer.putString("B", encoder);
237         buffer.rewind();
238         while (buffer.hasRemaining()) {
239             decoder.decode(session, buffer, out);
240         }
241         assertEquals(4, out.getMessageQueue().size());
242         assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
243         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
244         assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
245         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
246     }
247     
248     @Test
249     public void verifyThatHeaderWithoutLeadingSpaceIsSupported() throws Exception {
250         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
251             public void flush(NextFilter nextFilter, IoSession session) {
252             }
253         };
254         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
255         buffer.putString("GET / HTTP/1.0\r\nHost:localhost\r\n\r\n", encoder);
256         buffer.rewind();
257         while (buffer.hasRemaining()) {
258             decoder.decode(session, buffer, out);
259         }
260         assertEquals(2, out.getMessageQueue().size());
261         HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
262         assertEquals("localhost", request.getHeader("host"));
263         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
264     }
265 
266     @Test
267     public void verifyThatLeadingSpacesAreRemovedFromHeader() throws Exception {
268         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
269             public void flush(NextFilter nextFilter, IoSession session) {
270             }
271         };
272         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
273         buffer.putString("GET / HTTP/1.0\r\nHost:  localhost\r\n\r\n", encoder);
274         buffer.rewind();
275         while (buffer.hasRemaining()) {
276             decoder.decode(session, buffer, out);
277         }
278         assertEquals(2, out.getMessageQueue().size());
279         HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
280         assertEquals("localhost", request.getHeader("host"));
281         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
282     }
283 
284     @Test
285     public void verifyThatTrailingSpacesAreRemovedFromHeader() throws Exception {
286         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
287             public void flush(NextFilter nextFilter, IoSession session) {
288             }
289         };
290         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
291         buffer.putString("GET / HTTP/1.0\r\nHost:localhost  \r\n\r\n", encoder);
292         buffer.rewind();
293         while (buffer.hasRemaining()) {
294             decoder.decode(session, buffer, out);
295         }
296         assertEquals(2, out.getMessageQueue().size());
297         HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
298         assertEquals("localhost", request.getHeader("host"));
299         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
300     }
301     
302     @Test
303     public void dosOnRequestWithAdditionalData() throws Exception {
304         AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
305             public void flush(NextFilter nextFilter, IoSession session) {
306             }
307         };
308         IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
309         buffer.putString("GET / HTTP/1.0\r\nHost:localhost  \r\n\r\ndummy", encoder);
310         buffer.rewind();
311         int prevBufferPosition = buffer.position();
312         while (buffer.hasRemaining()) {
313             decoder.decode(session, buffer, out);
314             assertNotEquals("Buffer at new position", prevBufferPosition, buffer.position());
315             prevBufferPosition = buffer.position();
316         }
317         assertEquals(2, out.getMessageQueue().size());
318         HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
319         assertEquals("localhost", request.getHeader("host"));
320         assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
321         session.removeAttribute(DECODER_STATE_ATT); // This test leaves session in HEAD state, crashing following test
322     }
323 }