View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.impl.nio.codecs;
29  
30  import java.io.ByteArrayInputStream;
31  import java.io.IOException;
32  import java.nio.channels.Channels;
33  import java.nio.channels.ReadableByteChannel;
34  import java.nio.charset.Charset;
35  
36  import org.apache.http.Consts;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpRequest;
39  import org.apache.http.HttpResponse;
40  import org.apache.http.HttpVersion;
41  import org.apache.http.config.MessageConstraints;
42  import org.apache.http.impl.nio.reactor.SessionInputBufferImpl;
43  import org.apache.http.nio.NHttpMessageParser;
44  import org.apache.http.nio.reactor.SessionInputBuffer;
45  import org.junit.Assert;
46  import org.junit.Test;
47  
48  /**
49   * Simple tests for {@link AbstractMessageParser}.
50   */
51  public class TestHttpMessageParser {
52  
53      private static ReadableByteChannel newChannel(final String s, final Charset charset) {
54          return Channels.newChannel(new ByteArrayInputStream(s.getBytes(charset)));
55      }
56  
57      private static ReadableByteChannel newChannel(final String s) {
58          return newChannel(s, Consts.ASCII);
59      }
60  
61      @Test
62      public void testSimpleParsing() throws Exception {
63          final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
64          final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
65          requestParser.fillBuffer(newChannel("GET /whatever HTTP/1.1\r\nSome header: stuff\r\n\r\n"));
66          final HttpRequest request = requestParser.parse();
67          Assert.assertNotNull(request);
68          Assert.assertEquals("/whatever", request.getRequestLine().getUri());
69          Assert.assertEquals(1, request.getAllHeaders().length);
70      }
71  
72      @Test
73      public void testParsingChunkedMessages() throws Exception {
74          final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
75          final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
76  
77          requestParser.fillBuffer(newChannel("GET /whatev"));
78          HttpRequest request = requestParser.parse();
79          Assert.assertNull(request);
80          requestParser.fillBuffer(newChannel("er HTTP/1.1\r"));
81          request = requestParser.parse();
82          Assert.assertNull(request);
83          requestParser.fillBuffer(newChannel("\nSome header: stuff\r\n\r\n"));
84          request = requestParser.parse();
85  
86          Assert.assertNotNull(request);
87          Assert.assertEquals("/whatever", request.getRequestLine().getUri());
88          Assert.assertEquals(1, request.getAllHeaders().length);
89  
90      }
91  
92      @Test
93      public void testParsingFoldedHeaders() throws Exception {
94          final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
95          final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
96  
97          requestParser.fillBuffer(newChannel("GET /whatev"));
98          HttpRequest request = requestParser.parse();
99          Assert.assertNull(request);
100         requestParser.fillBuffer(newChannel("er HTTP/1.1\r"));
101         request = requestParser.parse();
102         Assert.assertNull(request);
103         requestParser.fillBuffer(newChannel("\nSome header: stuff\r\n"));
104         request = requestParser.parse();
105         Assert.assertNull(request);
106         requestParser.fillBuffer(newChannel("   more\r\n"));
107         request = requestParser.parse();
108         Assert.assertNull(request);
109         requestParser.fillBuffer(newChannel("\tstuff\r\n"));
110         request = requestParser.parse();
111         Assert.assertNull(request);
112         requestParser.fillBuffer(newChannel("\r\n"));
113         request = requestParser.parse();
114 
115         Assert.assertNotNull(request);
116         Assert.assertEquals("/whatever", request.getRequestLine().getUri());
117         Assert.assertEquals(1, request.getAllHeaders().length);
118         Assert.assertEquals("stuff more stuff", request.getFirstHeader("Some header").getValue());
119     }
120 
121     @Test
122     public void testParsingBadlyFoldedFirstHeader() throws Exception {
123         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
124         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
125 
126         requestParser.fillBuffer(newChannel("GET /whatev"));
127         HttpRequest request = requestParser.parse();
128         Assert.assertNull(request);
129         requestParser.fillBuffer(newChannel("er HTTP/1.1\r"));
130         request = requestParser.parse();
131         Assert.assertNull(request);
132         requestParser.fillBuffer(newChannel("\n  Some header: stuff\r\n"));
133         request = requestParser.parse();
134         Assert.assertNull(request);
135         requestParser.fillBuffer(newChannel("   more stuff\r\n"));
136         request = requestParser.parse();
137         Assert.assertNull(request);
138         requestParser.fillBuffer(newChannel("\r\n"));
139         request = requestParser.parse();
140 
141         Assert.assertNotNull(request);
142         Assert.assertEquals("/whatever", request.getRequestLine().getUri());
143         Assert.assertEquals(1, request.getAllHeaders().length);
144         Assert.assertEquals("stuff more stuff", request.getFirstHeader("Some header").getValue());
145     }
146 
147     @Test
148     public void testParsingEmptyFoldedHeader() throws Exception {
149         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
150         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
151 
152         requestParser.fillBuffer(newChannel("GET /whatev"));
153         HttpRequest request = requestParser.parse();
154         Assert.assertNull(request);
155         requestParser.fillBuffer(newChannel("er HTTP/1.1\r"));
156         request = requestParser.parse();
157         Assert.assertNull(request);
158         requestParser.fillBuffer(newChannel("\n  Some header: stuff\r\n"));
159         request = requestParser.parse();
160         Assert.assertNull(request);
161         requestParser.fillBuffer(newChannel("      \r\n"));
162         request = requestParser.parse();
163         Assert.assertNull(request);
164         requestParser.fillBuffer(newChannel("      more stuff\r\n"));
165         request = requestParser.parse();
166         Assert.assertNull(request);
167         requestParser.fillBuffer(newChannel("\r\n"));
168         request = requestParser.parse();
169 
170         Assert.assertNotNull(request);
171         Assert.assertEquals("/whatever", request.getRequestLine().getUri());
172         Assert.assertEquals(1, request.getAllHeaders().length);
173         Assert.assertEquals("stuff  more stuff", request.getFirstHeader("Some header").getValue());
174     }
175 
176     @Test
177     public void testParsingIncompleteRequestLine() throws Exception {
178         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
179         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
180 
181         final ReadableByteChannel channel = newChannel("GET /whatever HTTP/1.0");
182         requestParser.fillBuffer(channel);
183         requestParser.fillBuffer(channel);
184         final HttpRequest request = requestParser.parse();
185         Assert.assertNotNull(request);
186         Assert.assertEquals(HttpVersion.HTTP_1_0, request.getRequestLine().getProtocolVersion());
187     }
188 
189     @Test
190     public void testParsingIncompleteHeader() throws Exception {
191         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
192         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
193 
194         final ReadableByteChannel channel = newChannel("GET /whatever HTTP/1.0\r\nHeader: whatever");
195         requestParser.fillBuffer(channel);
196         requestParser.fillBuffer(channel);
197         final HttpRequest request = requestParser.parse();
198         Assert.assertNotNull(request);
199         Assert.assertEquals(1, request.getAllHeaders().length);
200         Assert.assertEquals("whatever", request.getFirstHeader("Header").getValue());
201     }
202 
203     @Test
204     public void testParsingInvalidRequestLine() throws Exception {
205         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
206         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
207 
208         final ReadableByteChannel channel = newChannel("GET garbage\r\n");
209         requestParser.fillBuffer(channel);
210         try {
211             requestParser.parse();
212             Assert.fail("HttpException should have been thrown");
213         } catch (final HttpException ex) {
214             // expected
215         }
216     }
217 
218     @Test
219     public void testParsingInvalidStatusLine() throws Exception {
220         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
221         final NHttpMessageParser<HttpResponse> responseParser = new DefaultHttpResponseParser(inbuf);
222 
223         final ReadableByteChannel channel = newChannel("HTTP 200 OK\r\n");
224         responseParser.fillBuffer(channel);
225         try {
226             responseParser.parse();
227             Assert.fail("HttpException should have been thrown");
228         } catch (final HttpException ex) {
229             // expected
230         }
231     }
232 
233     @Test
234     public void testParsingInvalidHeader() throws Exception {
235         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
236         final NHttpMessageParser<HttpResponse> responseParser = new DefaultHttpResponseParser(inbuf);
237 
238         final ReadableByteChannel channel = newChannel("HTTP/1.0 200 OK\r\nstuff\r\n\r\n");
239         responseParser.fillBuffer(channel);
240         try {
241             responseParser.parse();
242             Assert.fail("HttpException should have been thrown");
243         } catch (final HttpException ex) {
244             // expected
245         }
246     }
247 
248     @Test
249     public void testResetParser() throws Exception {
250         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
251         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf);
252 
253         ReadableByteChannel channel = newChannel("GET /whatever HTTP/1.0\r\nHeader: one\r\n\r\n");
254         requestParser.fillBuffer(channel);
255         HttpRequest request = requestParser.parse();
256         Assert.assertNotNull(request);
257         Assert.assertEquals(1, request.getAllHeaders().length);
258         Assert.assertEquals("one", request.getFirstHeader("Header").getValue());
259 
260         requestParser.reset();
261 
262         channel = newChannel("GET /whatever HTTP/1.0\r\nHeader: two\r\n\r\n");
263         requestParser.fillBuffer(channel);
264         request = requestParser.parse();
265         Assert.assertNotNull(request);
266         Assert.assertEquals(1, request.getAllHeaders().length);
267         Assert.assertEquals("two", request.getFirstHeader("Header").getValue());
268     }
269 
270     @Test
271     public void testInvalidConstructor() {
272         try {
273             new DefaultHttpRequestParser(null);
274             Assert.fail("IllegalArgumentException should have been thrown");
275         } catch (final IllegalArgumentException ex) {
276             // ignore
277         }
278     }
279 
280     @Test
281     public void testLineLimitForStatus() throws Exception {
282         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
283         NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf,
284                 MessageConstraints.lineLen(0));
285         requestParser.fillBuffer(newChannel("GET /whatever HTTP/1.0\r\nHeader: one\r\n\r\n"));
286         requestParser.parse();
287         requestParser.reset();
288 
289         requestParser = new DefaultHttpRequestParser(inbuf, MessageConstraints.lineLen(15));
290         try {
291             requestParser.fillBuffer(newChannel("GET /loooooooooooooooong HTTP/1.0\r\nHeader: one\r\n\r\n"));
292             requestParser.parse();
293             Assert.fail("IOException should have been thrown");
294         } catch (final IOException expected) {
295         }
296     }
297 
298     @Test
299     public void testLineLimitForHeader() throws Exception {
300         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
301 
302         NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf,
303                 MessageConstraints.lineLen(0));
304         requestParser.fillBuffer(newChannel("GET /whatever HTTP/1.0\r\nHeader: one\r\n\r\n"));
305         requestParser.parse();
306         requestParser.reset();
307 
308         requestParser = new DefaultHttpRequestParser(inbuf, MessageConstraints.lineLen(15));
309         requestParser.fillBuffer(newChannel("GET / HTTP/1.0\r\nHeader: 9012345\r\n\r\n"));
310         requestParser.parse();
311         requestParser.reset();
312         try {
313             requestParser.fillBuffer(newChannel("GET / HTTP/1.0\r\nHeader: 90123456\r\n\r\n"));
314             requestParser.parse();
315             Assert.fail("IOException should have been thrown");
316         } catch (final IOException expected) {
317         }
318     }
319 
320     @Test
321     public void testLineLimitForFoldedHeader() throws Exception {
322         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
323 
324         final MessageConstraints constraints = MessageConstraints.custom()
325                 .setMaxHeaderCount(2).setMaxLineLength(15).build();
326         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf, constraints);
327         try {
328             requestParser.fillBuffer(newChannel("GET / HTTP/1.0\r\nHeader: 9012345\r\n" +
329                     " 23456789012345\r\n 23456789012345\r\n 23456789012345\r\n\r\n"));
330             requestParser.parse();
331             Assert.fail("IOException should have been thrown");
332         } catch (final IOException expected) {
333         }
334     }
335 
336     @Test
337     public void testMaxHeaderCount() throws Exception {
338         final SessionInputBuffer inbuf = new SessionInputBufferImpl(1024, 128, Consts.ASCII);
339 
340         final MessageConstraints constraints = MessageConstraints.custom()
341                 .setMaxHeaderCount(2).setMaxLineLength(-1).build();
342         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf, constraints);
343         requestParser.fillBuffer(newChannel("GET /whatever HTTP/1.0\r\nHeader: one\r\nHeader: two\r\n\r\n"));
344         requestParser.parse();
345         requestParser.reset();
346 
347         try {
348             requestParser.fillBuffer(newChannel("GET /whatever HTTP/1.0\r\nHeader: one\r\n" +
349                     "Header: two\r\nHeader: three\r\n\r\n"));
350             requestParser.parse();
351             Assert.fail("IOException should have been thrown");
352         } catch (final IOException expected) {
353         }
354     }
355 
356     @Test
357     public void testDetectLineLimitEarly() throws Exception {
358         final SessionInputBuffer inbuf = new SessionInputBufferImpl(2, 128, Consts.ASCII);
359 
360         final NHttpMessageParser<HttpRequest> requestParser = new DefaultHttpRequestParser(inbuf,
361                 MessageConstraints.lineLen(2));
362         final ReadableByteChannel channel = newChannel("GET / HTTP/1.0\r\nHeader: one\r\n\r\n");
363         Assert.assertEquals(2, requestParser.fillBuffer(channel));
364         Assert.assertNull(requestParser.parse());
365         Assert.assertEquals(4, requestParser.fillBuffer(channel));
366         try {
367             requestParser.parse();
368             Assert.fail("IOException should have been thrown");
369         } catch (final IOException expected) {
370         }
371     }
372 
373 }