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  package org.apache.hc.client5.http.impl;
28  
29  import org.apache.hc.core5.http.HttpHeaders;
30  import org.apache.hc.core5.http.HttpResponse;
31  import org.apache.hc.core5.http.HttpStatus;
32  import org.apache.hc.core5.http.ProtocolException;
33  import org.apache.hc.core5.http.ProtocolVersion;
34  import org.apache.hc.core5.http.message.BasicHttpResponse;
35  import org.apache.hc.core5.http.ssl.TLS;
36  import org.junit.jupiter.api.Assertions;
37  import org.junit.jupiter.api.BeforeEach;
38  import org.junit.jupiter.api.Test;
39  
40  /**
41   * Simple tests for {@link ProtocolSwitchStrategy}.
42   */
43  class TestProtocolSwitchStrategy {
44  
45      ProtocolSwitchStrategy switchStrategy;
46  
47      @BeforeEach
48      void setUp() {
49          switchStrategy = new ProtocolSwitchStrategy();
50      }
51  
52      @Test
53      void testSwitchToTLS() throws Exception {
54          final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
55          response1.addHeader(HttpHeaders.UPGRADE, "TLS");
56          Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response1));
57  
58          final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
59          response2.addHeader(HttpHeaders.UPGRADE, "TLS/1.3");
60          Assertions.assertEquals(TLS.V_1_3.getVersion(), switchStrategy.switchProtocol(response2));
61      }
62  
63      @Test
64      void testSwitchToHTTP11AndTLS() throws Exception {
65          final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
66          response1.addHeader(HttpHeaders.UPGRADE, "TLS, HTTP/1.1");
67          Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response1));
68  
69          final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
70          response2.addHeader(HttpHeaders.UPGRADE, ",, HTTP/1.1, TLS, ");
71          Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response2));
72  
73          final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
74          response3.addHeader(HttpHeaders.UPGRADE, "HTTP/1.1");
75          response3.addHeader(HttpHeaders.UPGRADE, "TLS/1.2");
76          Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response3));
77  
78          final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
79          response4.addHeader(HttpHeaders.UPGRADE, "HTTP/1.1");
80          response4.addHeader(HttpHeaders.UPGRADE, "TLS/1.2, TLS/1.3");
81          Assertions.assertEquals(TLS.V_1_3.getVersion(), switchStrategy.switchProtocol(response4));
82      }
83  
84      @Test
85      void testSwitchInvalid() {
86          final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
87          response1.addHeader(HttpHeaders.UPGRADE, "Crap");
88          Assertions.assertThrows(ProtocolException.class, () -> switchStrategy.switchProtocol(response1));
89  
90          final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
91          response2.addHeader(HttpHeaders.UPGRADE, "TLS, huh?");
92          Assertions.assertThrows(ProtocolException.class, () -> switchStrategy.switchProtocol(response2));
93  
94          final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
95          response3.addHeader(HttpHeaders.UPGRADE, ",,,");
96          Assertions.assertThrows(ProtocolException.class, () -> switchStrategy.switchProtocol(response3));
97      }
98  
99      @Test
100     void testNullToken() throws ProtocolException {
101         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
102         response.addHeader(HttpHeaders.UPGRADE, "TLS,");
103         response.addHeader(HttpHeaders.UPGRADE, null);
104         Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response));
105     }
106 
107     @Test
108     void testWhitespaceOnlyToken() throws ProtocolException {
109         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
110         response.addHeader(HttpHeaders.UPGRADE, "   , TLS");
111         Assertions.assertEquals(TLS.V_1_2.getVersion(), switchStrategy.switchProtocol(response));
112     }
113 
114     @Test
115     void testUnsupportedTlsVersion() throws Exception {
116         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
117         response.addHeader(HttpHeaders.UPGRADE, "TLS/1.4");
118         Assertions.assertEquals(new ProtocolVersion("TLS", 1, 4), switchStrategy.switchProtocol(response));
119     }
120 
121     @Test
122     void testUnsupportedTlsMajorVersion() throws Exception {
123         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
124         response.addHeader(HttpHeaders.UPGRADE, "TLS/2.0");
125         Assertions.assertEquals(new ProtocolVersion("TLS", 2, 0), switchStrategy.switchProtocol(response));
126     }
127 
128     @Test
129     void testUnsupportedHttpVersion() {
130         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
131         response.addHeader(HttpHeaders.UPGRADE, "HTTP/2.0");
132         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
133                 switchStrategy.switchProtocol(response));
134         Assertions.assertEquals("Unsupported protocol or HTTP version: HTTP/2.0", ex.getMessage());
135     }
136 
137     @Test
138     void testInvalidTlsFormat() {
139         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
140         response.addHeader(HttpHeaders.UPGRADE, "TLS/abc");
141         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
142                 switchStrategy.switchProtocol(response));
143         Assertions.assertEquals("Invalid TLS major version number; error at offset 7: <TLS/abc>", ex.getMessage());
144     }
145 
146     @Test
147     void testHttp11Only() {
148         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
149         response.addHeader(HttpHeaders.UPGRADE, "HTTP/1.1");
150         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
151                 switchStrategy.switchProtocol(response));
152         Assertions.assertEquals("Invalid protocol switch response: no TLS version found", ex.getMessage());
153     }
154 
155     @Test
156     void testSwitchToTlsValid_TLS_1_2() throws Exception {
157         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
158         response.addHeader(HttpHeaders.UPGRADE, "TLS/1.2");
159         final ProtocolVersion result = switchStrategy.switchProtocol(response);
160         Assertions.assertEquals(TLS.V_1_2.getVersion(), result);
161     }
162 
163     @Test
164     void testSwitchToTlsValid_TLS_1_0() throws Exception {
165         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
166         response.addHeader(HttpHeaders.UPGRADE, "TLS/1.0");
167         final ProtocolVersion result = switchStrategy.switchProtocol(response);
168         Assertions.assertEquals(TLS.V_1_0.getVersion(), result);
169     }
170 
171     @Test
172     void testSwitchToTlsValid_TLS_1_1() throws Exception {
173         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
174         response.addHeader(HttpHeaders.UPGRADE, "TLS/1.1");
175         final ProtocolVersion result = switchStrategy.switchProtocol(response);
176         Assertions.assertEquals(TLS.V_1_1.getVersion(), result);
177     }
178 
179     @Test
180     void testInvalidTlsFormat_NoSlash() {
181         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
182         response.addHeader(HttpHeaders.UPGRADE, "TLSv1");
183         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
184                 switchStrategy.switchProtocol(response));
185         Assertions.assertEquals("Unsupported or invalid protocol: TLSv1", ex.getMessage());
186     }
187 
188     @Test
189     void testSwitchToTlsValid_TLS_1() throws Exception {
190         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
191         response.addHeader(HttpHeaders.UPGRADE, "TLS/1");
192         final ProtocolVersion result = switchStrategy.switchProtocol(response);
193         Assertions.assertEquals(TLS.V_1_0.getVersion(), result);
194     }
195 
196     @Test
197     void testInvalidTlsFormat_MissingMajor() {
198         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
199         response.addHeader(HttpHeaders.UPGRADE, "TLS/.1");
200         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
201                 switchStrategy.switchProtocol(response));
202         Assertions.assertEquals("Invalid TLS major version number; error at offset 4: <TLS/.1>", ex.getMessage());
203     }
204 
205     @Test
206     void testMultipleHttp11Tokens() {
207         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
208         response.addHeader(HttpHeaders.UPGRADE, "HTTP/1.1, HTTP/1.1");
209         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
210                 switchStrategy.switchProtocol(response));
211         Assertions.assertEquals("Invalid protocol switch response: no TLS version found", ex.getMessage());
212     }
213 
214     @Test
215     void testMixedInvalidAndValidTokens() {
216         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
217         response.addHeader(HttpHeaders.UPGRADE, "Crap, TLS/1.2, Invalid");
218         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
219                 switchStrategy.switchProtocol(response));
220         Assertions.assertEquals("Unsupported or invalid protocol: Crap", ex.getMessage());
221     }
222 
223     @Test
224     void testInvalidTlsFormat_NoProtocolName() {
225         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_SWITCHING_PROTOCOLS);
226         response.addHeader(HttpHeaders.UPGRADE, ",,/1.1");
227         final ProtocolException ex = Assertions.assertThrows(ProtocolException.class, () ->
228                 switchStrategy.switchProtocol(response));
229         Assertions.assertEquals("Invalid protocol; error at offset 2: <,,/1.1>", ex.getMessage());
230     }
231 
232 }