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.hc.core5.http2.impl;
29  
30  import java.util.Arrays;
31  import java.util.List;
32  
33  import org.apache.hc.core5.http.Header;
34  import org.apache.hc.core5.http.HttpException;
35  import org.apache.hc.core5.http.HttpHost;
36  import org.apache.hc.core5.http.HttpRequest;
37  import org.apache.hc.core5.http.message.BasicHeader;
38  import org.apache.hc.core5.http.message.BasicHttpRequest;
39  import org.apache.hc.core5.net.URIAuthority;
40  import org.junit.jupiter.api.Assertions;
41  import org.junit.jupiter.api.Test;
42  
43  public class TestDefaultH2RequestConverter {
44  
45      @Test
46      public void testConvertFromFieldsBasic() throws Exception {
47  
48          final List<Header> headers = Arrays.asList(
49                  new BasicHeader(":method", "GET"),
50                  new BasicHeader(":scheme", "http"),
51                  new BasicHeader(":authority", "www.example.com"),
52                  new BasicHeader(":path", "/"),
53                  new BasicHeader("custom123", "value"));
54  
55          final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
56          final HttpRequest request = converter.convert(headers);
57          Assertions.assertNotNull(request);
58          Assertions.assertEquals("GET", request.getMethod());
59          Assertions.assertEquals("http", request.getScheme());
60          Assertions.assertEquals(new URIAuthority("www.example.com"), request.getAuthority());
61          Assertions.assertEquals("/", request.getPath());
62          final Header[] allHeaders = request.getHeaders();
63          Assertions.assertEquals(1, allHeaders.length);
64          Assertions.assertEquals("custom123", allHeaders[0].getName());
65          Assertions.assertEquals("value", allHeaders[0].getValue());
66      }
67  
68      @Test
69      public void testConvertFromFieldsUpperCaseHeaderName() throws Exception {
70          final List<Header> headers = Arrays.asList(
71                  new BasicHeader(":method", "GET"),
72                  new BasicHeader(":scheme", "http"),
73                  new BasicHeader(":authority", "www.example.com"),
74                  new BasicHeader(":Path", "/"),
75                  new BasicHeader("custom", "value"));
76  
77          final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
78          Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
79                  "Header name ':Path' is invalid (header name contains uppercase characters)");
80      }
81  
82      @Test
83      public void testConvertFromFieldsConnectionHeader() throws Exception {
84          final List<Header> headers = Arrays.asList(
85                  new BasicHeader(":method", "GET"),
86                  new BasicHeader(":scheme", "http"),
87                  new BasicHeader(":authority", "www.example.com"),
88                  new BasicHeader(":path", "/"),
89                  new BasicHeader("connection", "keep-alive"));
90  
91          final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
92          Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
93                  "Header 'connection: keep-alive' is illegal for HTTP/2 messages");
94      }
95  
96      @Test
97      public void testConvertFromFieldsPseudoHeaderSequence() throws Exception {
98          final List<Header> headers = Arrays.asList(
99                  new BasicHeader(":method", "GET"),
100                 new BasicHeader(":scheme", "http"),
101                 new BasicHeader("custom", "value"),
102                 new BasicHeader(":authority", "www.example.com"),
103                 new BasicHeader(":path", "/"));
104 
105         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
106         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
107                 "Invalid sequence of headers (pseudo-headers must precede message headers)");
108     }
109 
110     @Test
111     public void testConvertFromFieldsMissingMethod() throws Exception {
112         final List<Header> headers = Arrays.asList(
113                 new BasicHeader(":scheme", "http"),
114                 new BasicHeader(":authority", "www.example.com"),
115                 new BasicHeader(":path", "/"),
116                 new BasicHeader("custom", "value"));
117 
118         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
119         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
120                 "Mandatory request header ':method' not found");
121     }
122 
123     @Test
124     public void testConvertFromFieldsMissingScheme() throws Exception {
125         final List<Header> headers = Arrays.asList(
126                 new BasicHeader(":method", "GET"),
127                 new BasicHeader(":authority", "www.example.com"),
128                 new BasicHeader(":path", "/"),
129                 new BasicHeader("custom", "value"));
130 
131         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
132         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
133                 "Mandatory request header ':scheme' not found");
134     }
135 
136     @Test
137     public void testConvertFromFieldsMissingPath() throws Exception {
138         final List<Header> headers = Arrays.asList(
139                 new BasicHeader(":method", "GET"),
140                 new BasicHeader(":scheme", "http"),
141                 new BasicHeader(":authority", "www.example.com"),
142                 new BasicHeader("custom", "value"));
143 
144         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
145         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
146                 "Mandatory request header ':path' not found");
147     }
148 
149     @Test
150     public void testConvertFromFieldsUnknownPseudoHeader() throws Exception {
151         final List<Header> headers = Arrays.asList(
152                 new BasicHeader(":method", "GET"),
153                 new BasicHeader(":scheme", "http"),
154                 new BasicHeader(":authority", "www.example.com"),
155                 new BasicHeader(":path", "/"),
156                 new BasicHeader(":custom", "value"));
157 
158         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
159         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
160                 "Unsupported request header ':custom'");
161     }
162 
163     @Test
164     public void testConvertFromFieldsMultipleMethod() throws Exception {
165         final List<Header> headers = Arrays.asList(
166                 new BasicHeader(":method", "GET"),
167                 new BasicHeader(":method", "GET"),
168                 new BasicHeader(":scheme", "http"),
169                 new BasicHeader(":authority", "www.example.com"),
170                 new BasicHeader(":path", "/"),
171                 new BasicHeader("custom", "value"));
172 
173         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
174         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
175                 "Multiple ':method' request headers are illegal");
176     }
177 
178     @Test
179     public void testConvertFromFieldsMultipleScheme() throws Exception {
180         final List<Header> headers = Arrays.asList(
181                 new BasicHeader(":method", "GET"),
182                 new BasicHeader(":scheme", "http"),
183                 new BasicHeader(":scheme", "https"),
184                 new BasicHeader(":authority", "www.example.com"),
185                 new BasicHeader(":path", "/"),
186                 new BasicHeader("custom", "value"));
187 
188         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
189         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
190                 "Multiple ':scheme' request headers are illegal");
191     }
192 
193     @Test
194     public void testConvertFromFieldsMultiplePath() throws Exception {
195         final List<Header> headers = Arrays.asList(
196                 new BasicHeader(":method", "GET"),
197                 new BasicHeader(":scheme", "https"),
198                 new BasicHeader(":authority", "www.example.com"),
199                 new BasicHeader(":path", "/"),
200                 new BasicHeader(":path", "/"),
201                 new BasicHeader("custom", "value"));
202 
203         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
204         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
205                 "Multiple ':path' request headers are illegal");
206     }
207 
208     @Test
209     public void testConvertFromFieldsConnect() throws Exception {
210 
211         final List<Header> headers = Arrays.asList(
212                 new BasicHeader(":method", "CONNECT"),
213                 new BasicHeader(":authority", "www.example.com"),
214                 new BasicHeader("custom", "value"));
215 
216         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
217         converter.convert(headers);
218     }
219 
220     @Test
221     public void testConvertFromFieldsConnectMissingAuthority() throws Exception {
222         final List<Header> headers = Arrays.asList(
223                 new BasicHeader(":method", "CONNECT"),
224                 new BasicHeader("custom", "value"));
225 
226         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
227         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
228                 "Header ':authority' is mandatory for CONNECT request");
229     }
230 
231     @Test
232     public void testConvertFromFieldsConnectPresentScheme() throws Exception {
233         final List<Header> headers = Arrays.asList(
234                 new BasicHeader(":method", "CONNECT"),
235                 new BasicHeader(":scheme", "http"),
236                 new BasicHeader(":authority", "www.example.com"),
237                 new BasicHeader("custom", "value"));
238 
239         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
240         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
241                 "Header ':scheme' must not be set for CONNECT request");
242     }
243 
244     @Test
245     public void testConvertFromFieldsConnectPresentPath() throws Exception {
246         final List<Header> headers = Arrays.asList(
247                 new BasicHeader(":method", "CONNECT"),
248                 new BasicHeader(":authority", "www.example.com"),
249                 new BasicHeader(":path", "/"),
250                 new BasicHeader("custom", "value"));
251 
252         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
253         Assertions.assertThrows(HttpException.class, () -> converter.convert(headers),
254                 "Header ':path' must not be set for CONNECT request");
255     }
256 
257     @Test
258     public void testConvertFromMessageBasic() throws Exception {
259 
260         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
261         request.addHeader("custom123", "Value");
262 
263         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
264         final List<Header> headers = converter.convert(request);
265 
266         Assertions.assertNotNull(headers);
267         Assertions.assertEquals(5, headers.size());
268         final Header header1 = headers.get(0);
269         Assertions.assertEquals(":method", header1.getName());
270         Assertions.assertEquals("GET", header1.getValue());
271         final Header header2 = headers.get(1);
272         Assertions.assertEquals(":scheme", header2.getName());
273         Assertions.assertEquals("http", header2.getValue());
274         final Header header3 = headers.get(2);
275         Assertions.assertEquals(":authority", header3.getName());
276         Assertions.assertEquals("host", header3.getValue());
277         final Header header4 = headers.get(3);
278         Assertions.assertEquals(":path", header4.getName());
279         Assertions.assertEquals("/", header4.getValue());
280         final Header header5 = headers.get(4);
281         Assertions.assertEquals("custom123", header5.getName());
282         Assertions.assertEquals("Value", header5.getValue());
283     }
284 
285     @Test
286     public void testConvertFromMessageMissingScheme() throws Exception {
287         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
288         request.addHeader("Custom123", "Value");
289         request.setScheme(null);
290 
291         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
292         Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Request scheme is not set");
293     }
294 
295     @Test
296     public void testConvertFromMessageMissingPath() throws Exception {
297         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
298         request.addHeader("Custom123", "Value");
299         request.setPath(null);
300 
301         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
302         Assertions.assertThrows(HttpException.class, () -> converter.convert(request), "Request path is not set");
303     }
304 
305     @Test
306     public void testConvertFromMessageConnect() throws Exception {
307 
308         final HttpRequest request = new BasicHttpRequest("CONNECT", new HttpHost("host:80"), null);
309         request.addHeader("custom123", "Value");
310 
311         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
312         final List<Header> headers = converter.convert(request);
313 
314         Assertions.assertNotNull(headers);
315         Assertions.assertEquals(3, headers.size());
316         final Header header1 = headers.get(0);
317         Assertions.assertEquals(":method", header1.getName());
318         Assertions.assertEquals("CONNECT", header1.getValue());
319         final Header header2 = headers.get(1);
320         Assertions.assertEquals(":authority", header2.getName());
321         Assertions.assertEquals("host:80", header2.getValue());
322         final Header header3 = headers.get(2);
323         Assertions.assertEquals("custom123", header3.getName());
324         Assertions.assertEquals("Value", header3.getValue());
325     }
326 
327     @Test
328     public void testConvertFromMessageConnectMissingAuthority() throws Exception {
329         final HttpRequest request = new BasicHttpRequest("CONNECT", null, null);
330         request.addHeader("Custom123", "Value");
331 
332         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
333         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
334                 "CONNECT request authority is not set");
335     }
336 
337     @Test
338     public void testConvertFromMessageConnectWithPath() throws Exception {
339         final HttpRequest request = new BasicHttpRequest("CONNECT", "/");
340         request.setAuthority(new URIAuthority("host"));
341         request.addHeader("Custom123", "Value");
342 
343         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
344         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
345                 "CONNECT request path must be null");
346     }
347 
348     @Test
349     public void testConvertFromMessageConnectionHeader() throws Exception {
350         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
351         request.addHeader("Connection", "Keep-Alive");
352 
353         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
354         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
355                 "Header 'Connection: Keep-Alive' is illegal for HTTP/2 messages");
356     }
357 
358     @Test
359     public void testConvertFromFieldsKeepAliveHeader() throws Exception {
360         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
361         request.addHeader("Keep-Alive", "timeout=5, max=1000");
362 
363         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
364         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
365                 "Header 'Keep-Alive: timeout=5, max=1000' is illegal for HTTP/2 messages");
366     }
367 
368     @Test
369     public void testConvertFromFieldsProxyConnectionHeader() throws Exception {
370         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
371         request.addHeader("Proxy-Connection", "keep-alive");
372 
373         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
374         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
375                 "Header 'Proxy-Connection: Keep-Alive' is illegal for HTTP/2 messages");
376     }
377 
378     @Test
379     public void testConvertFromFieldsTransferEncodingHeader() throws Exception {
380         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
381         request.addHeader("Transfer-Encoding", "gzip");
382 
383         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
384         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
385                 "Header 'Transfer-Encoding: gzip' is illegal for HTTP/2 messages");
386     }
387 
388     @Test
389     public void testConvertFromFieldsHostHeader() throws Exception {
390         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
391         request.addHeader("Host", "host");
392 
393         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
394         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
395                 "Header 'Host: host' is illegal for HTTP/2 messages");
396     }
397 
398     @Test
399     public void testConvertFromFieldsUpgradeHeader() throws Exception {
400         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
401         request.addHeader("Upgrade", "example/1, foo/2");
402 
403         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
404         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
405                 "Header 'Upgrade: example/1, foo/2' is illegal for HTTP/2 messages");
406     }
407 
408     @Test
409     public void testConvertFromFieldsTEHeader() throws Exception {
410         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
411         request.addHeader("TE", "gzip");
412 
413         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
414         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
415                 "Header 'TE: gzip' is illegal for HTTP/2 messages");
416     }
417 
418     @Test
419     public void testConvertFromFieldsTETrailerHeader() throws Exception {
420 
421         final List<Header> headers = Arrays.asList(
422             new BasicHeader(":method", "GET"),
423             new BasicHeader(":scheme", "http"),
424             new BasicHeader(":authority", "www.example.com"),
425             new BasicHeader(":path", "/"),
426             new BasicHeader("te", "trailers"));
427 
428         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
429         final HttpRequest request = converter.convert(headers);
430         Assertions.assertNotNull(request);
431         Assertions.assertEquals("GET", request.getMethod());
432         Assertions.assertEquals("http", request.getScheme());
433         Assertions.assertEquals(new URIAuthority("www.example.com"), request.getAuthority());
434         Assertions.assertEquals("/", request.getPath());
435         final Header[] allHeaders = request.getHeaders();
436         Assertions.assertEquals(1, allHeaders.length);
437         Assertions.assertEquals("te", allHeaders[0].getName());
438         Assertions.assertEquals("trailers", allHeaders[0].getValue());
439     }
440 
441     @Test
442     public void testConvertFromMessageInvalidHeader() throws Exception {
443         final HttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/");
444         request.addHeader(":custom", "stuff");
445 
446         final DefaultH2RequestConverter converter = new DefaultH2RequestConverter();
447         Assertions.assertThrows(HttpException.class, () -> converter.convert(request),
448                 "Header name ':custom' is invalid");
449     }
450 
451 }
452