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