001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *  
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *  
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License. 
018 *  
019 */
020package org.apache.mina.http;
021
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertTrue;
024
025import java.nio.charset.CharacterCodingException;
026import java.nio.charset.Charset;
027import java.nio.charset.CharsetEncoder;
028
029import org.apache.mina.core.buffer.IoBuffer;
030import org.apache.mina.core.filterchain.IoFilter.NextFilter;
031import org.apache.mina.core.session.DummySession;
032import org.apache.mina.core.session.IoSession;
033import org.apache.mina.filter.codec.AbstractProtocolDecoderOutput;
034import org.apache.mina.filter.codec.ProtocolDecoder;
035import org.apache.mina.http.api.HttpEndOfContent;
036import org.apache.mina.http.api.HttpRequest;
037import org.junit.Test;
038
039public class HttpServerDecoderTest {
040    private static final CharsetEncoder encoder = Charset.forName("US-ASCII").newEncoder(); //$NON-NLS-1$
041
042    private static final ProtocolDecoder decoder = new HttpServerDecoder();
043
044    /*
045     * Use a single session for all requests in order to test state management better
046     */
047    private static IoSession session = new DummySession();
048
049    /**
050     * Build an IO buffer containing a simple minimal HTTP request.
051     * 
052     * @param method the HTTP method
053     * @param body the option body
054     * @return the built IO buffer
055     * @throws CharacterCodingException if encoding fails
056     */
057    protected static IoBuffer getRequestBuffer(String method, String body) throws CharacterCodingException {
058        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
059        buffer.putString(method + " / HTTP/1.1\r\nHost: dummy\r\n", encoder);
060        
061        if (body != null) {
062            buffer.putString("Content-Length: " + body.length() + "\r\n\r\n", encoder);
063            buffer.putString(body, encoder);
064        } else {
065            buffer.putString("\r\n", encoder);
066        }
067        
068        buffer.rewind();
069        
070        return buffer;
071    }
072
073    protected static IoBuffer getRequestBuffer(String method) throws CharacterCodingException {
074        return getRequestBuffer(method, null);
075    }
076
077    /**
078     * Execute an HTPP request and return the queue of messages.
079     * 
080     * @param method the HTTP method
081     * @param body the optional body
082     * @return the protocol output and its queue of messages
083     * @throws Exception if error occurs (encoding,...)
084     */
085    protected static AbstractProtocolDecoderOutput executeRequest(String method, String body) throws Exception {
086        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
087            public void flush(NextFilter nextFilter, IoSession session) {
088            }
089        };
090
091        IoBuffer buffer = getRequestBuffer(method, body); //$NON-NLS-1$
092        
093        while (buffer.hasRemaining()) {
094            decoder.decode(session, buffer, out);
095        }
096        
097        return out;
098    }
099
100    @Test
101    public void testGetRequestWithoutBody() throws Exception {
102        AbstractProtocolDecoderOutput out = executeRequest("GET", null);
103        assertEquals(2, out.getMessageQueue().size());
104        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
105        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
106    }
107
108    @Test
109    public void testGetRequestBody() throws Exception {
110        AbstractProtocolDecoderOutput out = executeRequest("GET", "body");
111        assertEquals(3, out.getMessageQueue().size());
112        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
113        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
114        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
115    }
116
117    @Test
118    public void testPutRequestWithoutBody() throws Exception {
119        AbstractProtocolDecoderOutput out = executeRequest("PUT", null);
120        assertEquals(2, out.getMessageQueue().size());
121        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
122        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
123    }
124
125    @Test
126    public void testPutRequestBody() throws Exception {
127        AbstractProtocolDecoderOutput out = executeRequest("PUT", "body");
128        assertEquals(3, out.getMessageQueue().size());
129        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
130        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
131        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
132    }
133
134    @Test
135    public void testPostRequestWithoutBody() throws Exception {
136        AbstractProtocolDecoderOutput out = executeRequest("POST", null);
137        assertEquals(2, out.getMessageQueue().size());
138        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
139        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
140    }
141
142    @Test
143    public void testPostRequestBody() throws Exception {
144        AbstractProtocolDecoderOutput out = executeRequest("POST", "body");
145        assertEquals(3, out.getMessageQueue().size());
146        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
147        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
148        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
149    }
150
151    @Test
152    public void testDeleteRequestWithoutBody() throws Exception {
153        AbstractProtocolDecoderOutput out = executeRequest("DELETE", null);
154        assertEquals(2, out.getMessageQueue().size());
155        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
156        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
157    }
158
159    @Test
160    public void testDeleteRequestBody() throws Exception {
161        AbstractProtocolDecoderOutput out = executeRequest("DELETE", "body");
162        assertEquals(3, out.getMessageQueue().size());
163        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
164        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
165        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
166    }
167    
168    @Test
169    public void testDIRMINA965NoContent() throws Exception {
170        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
171            public void flush(NextFilter nextFilter, IoSession session) {
172            }
173        };
174        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
175        buffer.putString("GET / HTTP/1.1\r\nHost: ", encoder);
176        buffer.rewind();
177        while (buffer.hasRemaining()) {
178            decoder.decode(session, buffer, out);
179        }
180        buffer = IoBuffer.allocate(0).setAutoExpand(true);
181        buffer.putString("dummy\r\n\r\n", encoder);
182        buffer.rewind();
183        while (buffer.hasRemaining()) {
184            decoder.decode(session, buffer, out);
185        }
186        assertEquals(2, out.getMessageQueue().size());
187        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
188        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
189    }
190
191    @Test
192    public void testDIRMINA965WithContent() throws Exception {
193        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
194            public void flush(NextFilter nextFilter, IoSession session) {
195            }
196        };
197        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
198        buffer.putString("GET / HTTP/1.1\r\nHost: ", encoder);
199        buffer.rewind();
200        while (buffer.hasRemaining()) {
201            decoder.decode(session, buffer, out);
202        }
203        buffer = IoBuffer.allocate(0).setAutoExpand(true);
204        buffer.putString("dummy\r\nContent-Length: 1\r\n\r\nA", encoder);
205        buffer.rewind();
206        while (buffer.hasRemaining()) {
207            decoder.decode(session, buffer, out);
208        }
209        assertEquals(3, out.getMessageQueue().size());
210        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
211        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
212        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
213    }
214    @Test
215    public void testDIRMINA965WithContentOnTwoChunks() throws Exception {
216        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
217            public void flush(NextFilter nextFilter, IoSession session) {
218            }
219        };
220        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
221        buffer.putString("GET / HTTP/1.1\r\nHost: ", encoder);
222        buffer.rewind();
223        while (buffer.hasRemaining()) {
224            decoder.decode(session, buffer, out);
225        }
226        buffer = IoBuffer.allocate(0).setAutoExpand(true);
227        buffer.putString("dummy\r\nContent-Length: 2\r\n\r\nA", encoder);
228        buffer.rewind();
229        while (buffer.hasRemaining()) {
230            decoder.decode(session, buffer, out);
231        }
232        buffer = IoBuffer.allocate(0).setAutoExpand(true);
233        buffer.putString("B", encoder);
234        buffer.rewind();
235        while (buffer.hasRemaining()) {
236            decoder.decode(session, buffer, out);
237        }
238        assertEquals(4, out.getMessageQueue().size());
239        assertTrue(out.getMessageQueue().poll() instanceof HttpRequest);
240        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
241        assertTrue(out.getMessageQueue().poll() instanceof IoBuffer);
242        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
243    }
244    
245    @Test
246    public void verifyThatHeaderWithoutLeadingSpaceIsSupported() throws Exception {
247        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
248            public void flush(NextFilter nextFilter, IoSession session) {
249            }
250        };
251        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
252        buffer.putString("GET / HTTP/1.0\r\nHost:localhost\r\n\r\n", encoder);
253        buffer.rewind();
254        while (buffer.hasRemaining()) {
255            decoder.decode(session, buffer, out);
256        }
257        assertEquals(2, out.getMessageQueue().size());
258        HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
259        assertEquals("localhost", request.getHeader("host"));
260        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
261    }
262
263    @Test
264    public void verifyThatLeadingSpacesAreRemovedFromHeader() throws Exception {
265        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
266            public void flush(NextFilter nextFilter, IoSession session) {
267            }
268        };
269        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
270        buffer.putString("GET / HTTP/1.0\r\nHost:  localhost\r\n\r\n", encoder);
271        buffer.rewind();
272        while (buffer.hasRemaining()) {
273            decoder.decode(session, buffer, out);
274        }
275        assertEquals(2, out.getMessageQueue().size());
276        HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
277        assertEquals("localhost", request.getHeader("host"));
278        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
279    }
280
281    @Test
282    public void verifyThatTrailingSpacesAreRemovedFromHeader() throws Exception {
283        AbstractProtocolDecoderOutput out = new AbstractProtocolDecoderOutput() {
284            public void flush(NextFilter nextFilter, IoSession session) {
285            }
286        };
287        IoBuffer buffer = IoBuffer.allocate(0).setAutoExpand(true);
288        buffer.putString("GET / HTTP/1.0\r\nHost:localhost  \r\n\r\n", encoder);
289        buffer.rewind();
290        while (buffer.hasRemaining()) {
291            decoder.decode(session, buffer, out);
292        }
293        assertEquals(2, out.getMessageQueue().size());
294        HttpRequest request = (HttpRequest) out.getMessageQueue().poll();
295        assertEquals("localhost", request.getHeader("host"));
296        assertTrue(out.getMessageQueue().poll() instanceof HttpEndOfContent);
297    }
298}