View Javadoc
1   package org.apache.maven.it;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.it.util.ResourceExtractor;
23  import org.eclipse.jetty.server.Connector;
24  import org.eclipse.jetty.server.HttpConfiguration;
25  import org.eclipse.jetty.server.HttpConnectionFactory;
26  import org.eclipse.jetty.server.NetworkConnector;
27  import org.eclipse.jetty.server.Request;
28  import org.eclipse.jetty.server.SecureRequestCustomizer;
29  import org.eclipse.jetty.server.Server;
30  import org.eclipse.jetty.server.ServerConnector;
31  import org.eclipse.jetty.server.SslConnectionFactory;
32  import org.eclipse.jetty.server.handler.AbstractHandler;
33  import org.eclipse.jetty.util.ssl.SslContextFactory;
34  
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  import java.io.File;
38  import java.io.IOException;
39  import java.io.PrintWriter;
40  import java.util.List;
41  import java.util.Properties;
42  
43  import static org.eclipse.jetty.http.HttpVersion.HTTP_1_1;
44  
45  /**
46   * This is a test set for <a href="https://issues.apache.org/jira/browse/MNG-4428">MNG-4428</a>.
47   *
48   * @author Benjamin Bentmann
49   *
50   */
51  public class MavenITmng4428FollowHttpRedirectTest
52      extends AbstractMavenIntegrationTestCase
53  {
54  
55      public MavenITmng4428FollowHttpRedirectTest()
56      {
57          super( "[2.0.3,3.0-alpha-1),(3.0-alpha-1,)" );
58      }
59  
60      /**
61       * Verify that redirects from HTTP to HTTP are getting followed.
62       *
63       * @throws Exception in case of failure
64       */
65      public void testitHttpToHttp()
66          throws Exception
67      {
68          testit( true, true );
69      }
70  
71      /**
72       * Verify that redirects from HTTPS to HTTPS are getting followed.
73       *
74       * @throws Exception in case of failure
75       */
76      public void testitHttpsToHttps()
77          throws Exception
78      {
79          testit( false, false );
80      }
81  
82      /**
83       * Verify that redirects from HTTP to HTTPS are getting followed.
84       *
85       * @throws Exception in case of failure
86       */
87      public void testitHttpToHttps()
88          throws Exception
89      {
90          requiresMavenVersion( "[2.2.0]" );
91  
92          testit( true, false );
93      }
94  
95      /**
96       * Verify that redirects from HTTPS to HTTP are getting followed.
97       *
98       * @throws Exception in case of failure
99       */
100     public void testitHttpsToHttp()
101         throws Exception
102     {
103         requiresMavenVersion( "[2.2.0]" );
104 
105         testit( false, true );
106     }
107 
108     /**
109      * Verify that redirects using a relative location URL are getting followed. While a relative URL violates the
110      * HTTP spec, popular HTTP clients do support them so we better do, too.
111      *
112      * @throws Exception in case of failure
113      */
114     public void testitRelativeLocation()
115         throws Exception
116     {
117         testit( true, true, true );
118     }
119 
120     private void testit( boolean fromHttp, boolean toHttp )
121         throws Exception
122     {
123         testit( fromHttp, toHttp, false );
124     }
125 
126     private void testit( boolean fromHttp, boolean toHttp, boolean relativeLocation )
127         throws Exception
128     {
129         File testDir = ResourceExtractor.simpleExtractResources( getClass(), "/mng-4428" );
130 
131         Verifier verifier = newVerifier( testDir.getAbsolutePath() );
132 
133         // NOTE: trust store cannot be reliably configured for the current JVM
134         verifier.setForkJvm( true );
135 
136         // keytool -genkey -alias localhost -keypass key-passwd -keystore keystore -storepass store-passwd \
137         //   -validity 4096 -dname "cn=localhost, ou=None, L=Seattle, ST=Washington, o=ExampleOrg, c=US" -keyalg RSA
138         String storePath = new File( testDir, "keystore" ).getAbsolutePath();
139         String storePwd = "store-passwd";
140         String keyPwd = "key-passwd";
141 
142         Server server = new Server( 0 );
143         addHttpsConnector( server, storePath, storePwd, keyPwd );
144         Connector from = server.getConnectors()[ fromHttp ? 0 : 1 ];
145         Connector to = server.getConnectors()[ toHttp ? 0 : 1 ];
146         server.setHandler(
147                 new RedirectHandler( toHttp ? "http" : "https", relativeLocation ? null : (NetworkConnector) to ) );
148 
149         try
150         {
151             server.start();
152             if ( server.isFailed() )
153             {
154                 fail( "Couldn't bind the server socket to a free port!" );
155             }
156             verifier.setAutoclean( false );
157             verifier.deleteArtifacts( "org.apache.maven.its.mng4428" );
158             verifier.deleteDirectory( "target" );
159             Properties filterProps = verifier.newDefaultFilterProperties();
160             filterProps.setProperty( "@protocol@", fromHttp ? "http" : "https" );
161             filterProps.setProperty( "@port@", Integer.toString( ( (NetworkConnector) from ).getLocalPort() ) );
162             verifier.filterFile( "settings-template.xml", "settings.xml", "UTF-8", filterProps );
163             verifier.addCliOption( "-X --settings" );
164             verifier.addCliOption( "settings.xml" );
165             verifier.setSystemProperty( "javax.net.ssl.trustStore", storePath );
166             verifier.setSystemProperty( "javax.net.ssl.trustStorePassword", storePwd );
167             verifier.setLogFileName( "log-" + getName().substring( 6 ) + ".txt" );
168             verifier.executeGoal( "validate" );
169             verifier.verifyErrorFreeLog();
170             verifier.resetStreams();
171         }
172         finally
173         {
174             server.stop();
175             server.join();
176         }
177 
178         List<String> cp = verifier.loadLines( "target/classpath.txt", "UTF-8" );
179         assertTrue( cp.toString(), cp.contains( "dep-0.1.jar" ) );
180     }
181 
182     private void addHttpsConnector( Server server, String keyStorePath, String keyStorePassword, String keyPassword )
183     {
184         SslContextFactory sslContextFactory = new SslContextFactory( keyStorePath );
185         sslContextFactory.setKeyStorePassword( keyStorePassword );
186         sslContextFactory.setKeyManagerPassword( keyPassword );
187         HttpConfiguration httpConfiguration = new HttpConfiguration();
188         httpConfiguration.setSecureScheme( "https" );
189         HttpConfiguration httpsConfiguration = new HttpConfiguration( httpConfiguration );
190         httpsConfiguration.addCustomizer( new SecureRequestCustomizer() );
191         ServerConnector httpsConnector = new ServerConnector( server,
192                 new SslConnectionFactory( sslContextFactory, HTTP_1_1.asString() ),
193                 new HttpConnectionFactory( httpsConfiguration ) );
194         server.addConnector( httpsConnector );
195     }
196 
197     static class RedirectHandler extends AbstractHandler
198     {
199         private final String protocol;
200 
201         private final NetworkConnector connector;
202 
203         RedirectHandler( String protocol, NetworkConnector connector )
204         {
205             this.protocol = protocol;
206             this.connector = connector;
207         }
208 
209         public void handle( String target, Request baseRequest, HttpServletRequest request,
210                             HttpServletResponse response )
211             throws IOException
212         {
213             System.out.println( "Handling " + request.getMethod() + " " + request.getRequestURL() );
214 
215             PrintWriter writer = response.getWriter();
216 
217             String uri = request.getRequestURI();
218             if ( uri.startsWith( "/repo/" ) )
219             {
220                 String location = "/redirected/" + uri.substring( 6 );
221                 if ( protocol != null && connector != null )
222                 {
223                     location = protocol + "://localhost:" + connector.getLocalPort() + location;
224                 }
225                 if ( uri.endsWith( ".pom" ) )
226                 {
227                     response.setStatus( HttpServletResponse.SC_MOVED_TEMPORARILY );
228                 }
229                 else
230                 {
231                     response.setStatus( HttpServletResponse.SC_MOVED_PERMANENTLY );
232                 }
233                 response.setHeader( "Location", location );
234             }
235             else if ( uri.endsWith( ".pom" ) )
236             {
237                 writer.println( "<project>" );
238                 writer.println( "  <modelVersion>4.0.0</modelVersion>" );
239                 writer.println( "  <groupId>org.apache.maven.its.mng4428</groupId>" );
240                 writer.println( "  <artifactId>dep</artifactId>" );
241                 writer.println( "  <version>0.1</version>" );
242                 writer.println( "</project>" );
243                 response.setStatus( HttpServletResponse.SC_OK );
244             }
245             else if ( uri.endsWith( ".jar" ) )
246             {
247                 writer.println( "empty" );
248                 response.setStatus( HttpServletResponse.SC_OK );
249             }
250             else
251             {
252                 response.setStatus( HttpServletResponse.SC_NOT_FOUND );
253             }
254 
255             ( (Request) request ).setHandled( true );
256         }
257     }
258 }