View Javadoc
1   package org.apache.maven.wagon.http;
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.wagon.FileTestUtils;
23  import org.apache.maven.wagon.ResourceDoesNotExistException;
24  import org.apache.maven.wagon.StreamingWagon;
25  import org.apache.maven.wagon.StreamingWagonTestCase;
26  import org.apache.maven.wagon.TransferFailedException;
27  import org.apache.maven.wagon.Wagon;
28  import org.apache.maven.wagon.WagonException;
29  import org.apache.maven.wagon.authentication.AuthenticationInfo;
30  import org.apache.maven.wagon.authorization.AuthorizationException;
31  import org.apache.maven.wagon.proxy.ProxyInfo;
32  import org.apache.maven.wagon.proxy.ProxyInfoProvider;
33  import org.apache.maven.wagon.repository.Repository;
34  import org.apache.maven.wagon.resource.Resource;
35  import org.codehaus.plexus.util.FileUtils;
36  import org.codehaus.plexus.util.IOUtil;
37  import org.codehaus.plexus.util.StringUtils;
38  import org.eclipse.jetty.http.HttpStatus;
39  import org.eclipse.jetty.security.ConstraintMapping;
40  import org.eclipse.jetty.security.ConstraintSecurityHandler;
41  import org.eclipse.jetty.security.HashLoginService;
42  import org.eclipse.jetty.security.SecurityHandler;
43  import org.eclipse.jetty.security.authentication.BasicAuthenticator;
44  import org.eclipse.jetty.server.Connector;
45  import org.eclipse.jetty.server.HttpConfiguration;
46  import org.eclipse.jetty.server.HttpConnectionFactory;
47  import org.eclipse.jetty.server.Request;
48  import org.eclipse.jetty.server.Response;
49  import org.eclipse.jetty.server.Server;
50  import org.eclipse.jetty.server.ServerConnector;
51  import org.eclipse.jetty.server.handler.AbstractHandler;
52  import org.eclipse.jetty.server.handler.HandlerCollection;
53  import org.eclipse.jetty.servlet.DefaultServlet;
54  import org.eclipse.jetty.servlet.ServletContextHandler;
55  import org.eclipse.jetty.servlet.ServletHolder;
56  import org.eclipse.jetty.util.security.Constraint;
57  import org.eclipse.jetty.util.security.Password;
58  
59  import javax.servlet.ServletException;
60  import javax.servlet.http.HttpServletRequest;
61  import javax.servlet.http.HttpServletResponse;
62  import java.io.ByteArrayOutputStream;
63  import java.io.File;
64  import java.io.FileInputStream;
65  import java.io.FileOutputStream;
66  import java.io.IOException;
67  import java.io.InputStream;
68  import java.io.OutputStream;
69  import java.lang.reflect.Method;
70  import java.net.URLDecoder;
71  import java.util.ArrayList;
72  import java.util.Collections;
73  import java.util.Enumeration;
74  import java.util.HashMap;
75  import java.util.List;
76  import java.util.Map;
77  import java.util.Properties;
78  import java.util.concurrent.atomic.AtomicBoolean;
79  import java.util.zip.DeflaterOutputStream;
80  import java.util.zip.GZIPOutputStream;
81  
82  /**
83   *
84   */
85  public abstract class HttpWagonTestCase
86      extends StreamingWagonTestCase
87  {
88      public static final int SC_TOO_MANY_REQUESTS = 429;
89  
90      private Server server;
91      private ServerConnector connector;
92  
93      protected int getLocalPort( Server server )
94      {
95          Connector connector = server.getConnectors()[0];
96          return ( ( ServerConnector ) connector ).getLocalPort();
97      }
98  
99      protected void setupWagonTestingFixtures()
100         throws Exception
101     {
102         // File round trip testing
103 
104         File file = FileTestUtils.createUniqueFile( "local-repository", "test-resource" );
105 
106         file.delete();
107 
108         file.getParentFile().mkdirs();
109 
110         File repositoryDirectory = getRepositoryDirectory();
111         FileUtils.deleteDirectory( repositoryDirectory );
112         repositoryDirectory.mkdirs();
113 
114         server = new Server( );
115         //connector = new ServerConnector( server, new HttpConnectionFactory( new HttpConfiguration() ) );
116         //server.addConnector( connector );
117         connector = addConnector( server );
118 
119         PutHandler putHandler = new PutHandler( repositoryDirectory );
120 
121         ServletContextHandler context = createContext( server, repositoryDirectory );
122         HandlerCollection handlers = new HandlerCollection();
123         handlers.addHandler( putHandler );
124         handlers.addHandler( context );
125         server.setHandler( handlers );
126 
127         server.start();
128     }
129 
130     protected final int getTestRepositoryPort()
131     {
132         if ( server == null )
133         {
134             return 0;
135         }
136         return connector.getLocalPort();
137     }
138 
139     protected ServletContextHandler createContext( Server server, File repositoryDirectory )
140         throws IOException
141     {
142         ServletContextHandler root = new ServletContextHandler( ServletContextHandler.SESSIONS );
143         root.setResourceBase( repositoryDirectory.getAbsolutePath() );
144         ServletHolder servletHolder = new ServletHolder( new DefaultServlet() );
145         root.addServlet( servletHolder, "/*" );
146         return root;
147     }
148 
149     protected void tearDownWagonTestingFixtures()
150         throws Exception
151     {
152         server.stop();
153     }
154 
155     public void testWagonGetFileList()
156         throws Exception
157     {
158         File dir = getRepositoryDirectory();
159         FileUtils.deleteDirectory( dir );
160 
161         File f = new File( dir, "file-list" );
162         f.mkdirs();
163 
164         super.testWagonGetFileList();
165     }
166 
167     public void testHttpHeaders()
168         throws Exception
169     {
170         Properties properties = new Properties();
171         properties.setProperty( "User-Agent", "Maven-Wagon/1.0" );
172 
173         StreamingWagon wagon = (StreamingWagon) getWagon();
174 
175         setHttpHeaders( wagon, properties );
176 
177         Server server = new Server(  );
178         TestHeaderHandler handler = new TestHeaderHandler();
179         server.setHandler( handler );
180         ServerConnector serverConnector = addConnector( server );
181         server.start();
182 
183         wagon.connect(
184             new Repository( "id", getProtocol() + "://localhost:" + serverConnector.getLocalPort() ) );
185 
186         wagon.getToStream( "resource", new ByteArrayOutputStream() );
187 
188         wagon.disconnect();
189 
190         server.stop();
191 
192         assertEquals( "Maven-Wagon/1.0", handler.headers.get( "User-Agent" ) );
193     }
194 
195     /**
196      * test set of User-Agent as it's done by aether wagon connector with using setHttpHeaders
197      */
198     public void testHttpHeadersWithCommonMethods()
199         throws Exception
200     {
201         Properties properties = new Properties();
202         properties.setProperty( "User-Agent", "Maven-Wagon/1.0" );
203 
204         StreamingWagon wagon = (StreamingWagon) getWagon();
205 
206         Method setHttpHeaders = wagon.getClass().getMethod( "setHttpHeaders", Properties.class );
207         setHttpHeaders.invoke( wagon, properties );
208 
209         Server server = new Server( );
210         ServerConnector serverConnector = addConnector( server );
211         TestHeaderHandler handler = new TestHeaderHandler();
212         server.setHandler( handler );
213         addConnector( server );
214         server.start();
215 
216         wagon.connect(
217             new Repository( "id", getProtocol() + "://localhost:" + serverConnector.getLocalPort() ) );
218 
219         wagon.getToStream( "resource", new ByteArrayOutputStream() );
220 
221         wagon.disconnect();
222 
223         server.stop();
224 
225         assertEquals( "Maven-Wagon/1.0", handler.headers.get( "User-Agent" ) );
226     }
227 
228     public void testUserAgentHeaderIsPresentByDefault()
229         throws Exception
230     {
231         StreamingWagon wagon = (StreamingWagon) getWagon();
232         Server server = new Server(  );
233         TestHeaderHandler handler = new TestHeaderHandler();
234         server.setHandler( handler );
235         addConnector( server );
236         server.start();
237         wagon.connect( new Repository( "id", getProtocol() + "://localhost:" + getLocalPort( server ) ) );
238         wagon.getToStream( "resource", new ByteArrayOutputStream() );
239         wagon.disconnect();
240         server.stop();
241 
242         assertNotNull( "default User-Agent header of wagon provider should be present",
243                        handler.headers.get( "User-Agent" ) );
244     }
245 
246     public void testUserAgentHeaderIsPresentOnlyOnceIfSetMultipleTimes()
247         throws Exception
248     {
249         StreamingWagon wagon = (StreamingWagon) getWagon();
250 
251         // 1. set User-Agent header via HttpConfiguration
252         Properties headers1 = new Properties();
253         headers1.setProperty( "User-Agent", "test-user-agent" );
254         setHttpHeaders( wagon, headers1 );
255 
256         // 2. redundantly set User-Agent header via setHttpHeaders()
257         Properties headers2 = new Properties();
258         headers2.setProperty( "User-Agent", "test-user-agent" );
259         Method setHttpHeaders = wagon.getClass().getMethod( "setHttpHeaders", Properties.class );
260         setHttpHeaders.invoke( wagon, headers2 );
261 
262         Server server = new Server(  );
263         TestHeaderHandler handler = new TestHeaderHandler();
264         server.setHandler( handler );
265         addConnector( server );
266         server.start();
267         wagon.connect( new Repository( "id", getProtocol() + "://localhost:" + getLocalPort( server ) ) );
268         wagon.getToStream( "resource", new ByteArrayOutputStream() );
269         wagon.disconnect();
270         server.stop();
271 
272         assertEquals( "test-user-agent", handler.headers.get( "User-Agent" ) );
273 
274     }
275 
276     protected abstract void setHttpHeaders( StreamingWagon wagon, Properties properties );
277 
278     protected ServerConnector addConnector( Server server )
279     {
280         ServerConnector serverConnector =
281             new ServerConnector( server, new HttpConnectionFactory( new HttpConfiguration() ) );
282         server.addConnector( serverConnector );
283         return serverConnector;
284     }
285 
286     protected String getRepositoryUrl( Server server )
287     {
288         int localPort = getLocalPort( server );
289         return getProtocol() + "://localhost:" + localPort;
290     }
291 
292     public void testGetForbidden()
293         throws Exception
294     {
295         try
296         {
297             runTestGet( HttpServletResponse.SC_FORBIDDEN );
298             fail();
299         }
300         catch ( AuthorizationException e )
301         {
302             assertTrue( true );
303         }
304     }
305 
306     public void testGet404()
307         throws Exception
308     {
309         try
310         {
311             runTestGet( HttpServletResponse.SC_NOT_FOUND );
312             fail();
313         }
314         catch ( ResourceDoesNotExistException e )
315         {
316             assertTrue( true );
317         }
318     }
319 
320     public void testList429()
321         throws Exception
322     {
323         StreamingWagon wagon = (StreamingWagon) getWagon();
324         try
325         {
326 
327             Server server = new Server(  );
328             final AtomicBoolean called = new AtomicBoolean();
329 
330             AbstractHandler handler = new AbstractHandler()
331             {
332                 public void handle( String target, Request baseRequest, HttpServletRequest request,
333                     HttpServletResponse response ) throws IOException, ServletException
334                 {
335                     if ( called.get() )
336                     {
337                         response.setStatus( HttpServletResponse.SC_OK );
338                         baseRequest.setHandled( true );
339                     }
340                     else
341                     {
342                         called.set( true );
343                         response.setStatus( SC_TOO_MANY_REQUESTS );
344                         baseRequest.setHandled( true );
345 
346                     }
347                 }
348             };
349 
350             server.setHandler( handler );
351             ServerConnector serverConnector = addConnector( server );
352             server.start();
353 
354             wagon.connect( new Repository( "id", getRepositoryUrl( server ) ) );
355 
356             try
357             {
358                 wagon.getFileList( "resource" );
359             }
360             finally
361             {
362                 wagon.disconnect();
363 
364                 server.stop();
365             }
366 
367         }
368         catch ( ResourceDoesNotExistException e )
369         {
370             assertTrue( true );
371         }
372         catch ( TransferFailedException e )
373         {
374             if ( wagon.getClass().getName().contains( "Lightweight" ) )
375             {
376                 //we don't care about lightweight
377                 assertTrue( true );
378             }
379             else
380             {
381                 fail();
382             }
383 
384         }
385     }
386 
387     public void testGet500()
388         throws Exception
389     {
390         try
391         {
392             runTestGet( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
393             fail();
394         }
395         catch ( TransferFailedException e )
396         {
397             assertTrue( true );
398         }
399     }
400 
401     private void runTestGet( int status )
402         throws Exception
403     {
404         StreamingWagon wagon = (StreamingWagon) getWagon();
405 
406         Server server = createStatusServer( status );
407         server.start();
408 
409         String baseUrl = getRepositoryUrl( server );
410         String resourceName = "resource";
411         String serverReasonPhrase = HttpStatus.getCode( status ).getMessage();
412 
413         wagon.connect( new Repository( "id", baseUrl ) );
414 
415         try
416         {
417             wagon.getToStream( "resource", new ByteArrayOutputStream() );
418             fail();
419         }
420         catch ( Exception e )
421         {
422             verifyWagonExceptionMessage( e, status, baseUrl + "/" + resourceName, serverReasonPhrase );
423             throw e;
424         }
425         finally
426         {
427             wagon.disconnect();
428 
429             server.stop();
430         }
431     }
432 
433     public void testResourceExistsForbidden()
434         throws Exception
435     {
436         try
437         {
438             runTestResourceExists( HttpServletResponse.SC_FORBIDDEN );
439             fail();
440         }
441         catch ( AuthorizationException e )
442         {
443             assertTrue( true );
444         }
445     }
446 
447     public void testResourceExists404()
448         throws Exception
449     {
450         try
451         {
452             assertFalse( runTestResourceExists( HttpServletResponse.SC_NOT_FOUND ) );
453         }
454         catch ( ResourceDoesNotExistException e )
455         {
456             assertTrue( true );
457         }
458     }
459 
460     public void testResourceExists500()
461         throws Exception
462     {
463         try
464         {
465             runTestResourceExists( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
466             fail();
467         }
468         catch ( TransferFailedException e )
469         {
470             assertTrue( true );
471         }
472     }
473 
474     public void testResourceExists429()
475         throws Exception
476     {
477         try
478         {
479 
480             final AtomicBoolean called = new AtomicBoolean();
481 
482             AbstractHandler handler = new AbstractHandler()
483             {
484                 public void handle( String target, Request baseRequest, HttpServletRequest request,
485                     HttpServletResponse response ) throws IOException, ServletException
486                 {
487                     if ( called.get() )
488                     {
489                         response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
490                         baseRequest.setHandled( true );
491                     }
492                     else
493                     {
494                         called.set( true );
495                         response.setStatus( SC_TOO_MANY_REQUESTS );
496                         baseRequest.setHandled( true );
497                     }
498                 }
499             };
500 
501             StreamingWagon wagon = (StreamingWagon) getWagon();
502             Server server = new Server(  );
503             server.setHandler( handler );
504             addConnector( server );
505             server.start();
506             wagon.connect( new Repository( "id", getRepositoryUrl( server ) ) );
507 
508             try
509             {
510                 wagon.resourceExists( "resource" );
511             }
512             finally
513             {
514                 wagon.disconnect();
515 
516                 server.stop();
517             }
518 
519             fail();
520         }
521         catch ( TransferFailedException e )
522         {
523             assertTrue( true );
524         }
525     }
526 
527 
528     private boolean runTestResourceExists( int status )
529         throws Exception
530     {
531         StreamingWagon wagon = (StreamingWagon) getWagon();
532 
533         Server server = createStatusServer( status );
534         server.start();
535 
536         wagon.connect( new Repository( "id", getRepositoryUrl( server ) ) );
537 
538         try
539         {
540             return wagon.resourceExists( "resource" );
541         }
542         finally
543         {
544             wagon.disconnect();
545 
546             server.stop();
547         }
548     }
549 
550     protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
551     {
552         File file = new File( getRepositoryDirectory(), resource.getName() );
553         return ( file.lastModified() / 1000 ) * 1000;
554     }
555 
556     protected File getRepositoryDirectory()
557     {
558         return getTestFile( "target/test-output/http-repository" );
559     }
560 
561     public void testGzipGet()
562         throws Exception
563     {
564         Server server = new Server( );
565 
566         String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
567         ServletContextHandler root = new ServletContextHandler( ServletContextHandler.SESSIONS );
568         root.setResourceBase( localRepositoryPath );
569         ServletHolder servletHolder = new ServletHolder( new DefaultServlet() );
570         servletHolder.setInitParameter( "gzip", "true" );
571         root.addServlet( servletHolder, "/*" );
572         addConnector( server );
573         server.setHandler( root );
574         server.start();
575 
576         try
577         {
578             Wagon wagon = getWagon();
579 
580             Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
581 
582             File sourceFile = new File( localRepositoryPath + "/gzip" );
583 
584             sourceFile.deleteOnExit();
585 
586             String resName = "gzip-res.txt";
587             String sourceContent = writeTestFile( sourceFile, resName, "gzip" );
588 
589             wagon.connect( testRepository );
590 
591             File destFile = FileTestUtils.createUniqueFile( getName(), getName() );
592 
593             destFile.deleteOnExit();
594 
595             wagon.get( "gzip/" + resName, destFile );
596 
597             wagon.disconnect();
598 
599             String destContent = FileUtils.fileRead( destFile );
600 
601             assertEquals( sourceContent, destContent );
602         }
603         finally
604         {
605             server.stop();
606         }
607     }
608 
609     /* This test cannot be enabled because we cannot tell GzipFilter to compress with deflate only
610     public void testDeflateGet()
611             throws Exception
612         {
613             Server server = new Server( );
614 
615             String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
616             ServletContextHandler root = new ServletContextHandler( ServletContextHandler.SESSIONS );
617             root.setResourceBase( localRepositoryPath );
618             ServletHolder servletHolder = new ServletHolder( new DefaultServlet() );
619             root.addServlet( servletHolder, "/*" );
620             FilterHolder filterHolder = new FilterHolder( new GzipFilter() );
621             root.addFilter( filterHolder, "/deflate/*", EnumSet.of( DispatcherType.REQUEST ) );
622             addConnector( server );
623             server.setHandler( root );
624             server.start();
625 
626             try
627             {
628                 Wagon wagon = getWagon();
629 
630                 Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
631 
632                 File sourceFile = new File( localRepositoryPath + "/deflate" );
633 
634                 sourceFile.deleteOnExit();
635 
636                 String resName = "deflate-res.txt";
637                 String sourceContent = writeTestFile( sourceFile, resName, null );
638 
639                 wagon.connect( testRepository );
640 
641                 File destFile = FileTestUtils.createUniqueFile( getName(), getName() );
642 
643                 destFile.deleteOnExit();
644 
645                 wagon.get( "deflate/" + resName, destFile );
646 
647                 wagon.disconnect();
648 
649                 String destContent = FileUtils.fileRead( destFile );
650 
651                 assertEquals( sourceContent, destContent );
652             }
653             finally
654             {
655                 server.stop();
656             }
657         }*/
658 
659     public void testProxiedRequest()
660         throws Exception
661     {
662         ProxyInfo proxyInfo = createProxyInfo();
663         TestHeaderHandler handler = new TestHeaderHandler();
664 
665         runTestProxiedRequest( proxyInfo, handler );
666     }
667 
668     public void testProxiedRequestWithAuthentication()
669         throws Exception
670     {
671         ProxyInfo proxyInfo = createProxyInfo();
672         proxyInfo.setUserName( "user" );
673         proxyInfo.setPassword( "secret" );
674         AuthorizingProxyHandler handler = new AuthorizingProxyHandler();
675 
676         runTestProxiedRequest( proxyInfo, handler );
677 
678         assertTrue( handler.headers.containsKey( "Proxy-Authorization" ) );
679 
680         if ( supportProxyPreemptiveAuthentication() )
681         {
682             assertEquals( HttpServletResponse.SC_OK, handler.handlerRequestResponses.get( 0 ).responseCode );
683         }
684         else
685         {
686             assertEquals( HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED,
687                           handler.handlerRequestResponses.get( 0 ).responseCode );
688             assertEquals( HttpServletResponse.SC_OK, handler.handlerRequestResponses.get( 1 ).responseCode );
689         }
690 
691     }
692 
693     public void testProxiedRequestWithAuthenticationWithProvider()
694         throws Exception
695     {
696         final ProxyInfo proxyInfo = createProxyInfo();
697         proxyInfo.setUserName( "user" );
698         proxyInfo.setPassword( "secret" );
699         AuthorizingProxyHandler handler = new AuthorizingProxyHandler();
700 
701         ProxyInfoProvider proxyInfoProvider = new ProxyInfoProvider()
702         {
703             public ProxyInfo getProxyInfo( String protocol )
704             {
705                 return proxyInfo;
706             }
707         };
708         runTestProxiedRequestWithProvider( proxyInfoProvider, handler );
709 
710         assertTrue( handler.headers.containsKey( "Proxy-Authorization" ) );
711 
712         if ( supportProxyPreemptiveAuthentication() )
713         {
714             assertEquals( HttpServletResponse.SC_OK, handler.handlerRequestResponses.get( 0 ).responseCode );
715         }
716         else
717         {
718             assertEquals( HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED,
719                           handler.handlerRequestResponses.get( 0 ).responseCode );
720             assertEquals( HttpServletResponse.SC_OK, handler.handlerRequestResponses.get( 1 ).responseCode );
721         }
722 
723     }
724 
725     public void testRedirectGetToStream()
726         throws Exception
727     {
728         StreamingWagon wagon = (StreamingWagon) getWagon();
729 
730         Server realServer = new Server(  );
731         TestHeaderHandler handler = new TestHeaderHandler();
732 
733         realServer.setHandler( handler );
734         addConnector( realServer );
735         realServer.start();
736 
737         Server redirectServer = new Server(  );
738 
739         addConnector( redirectServer );
740 
741         String protocol = getProtocol();
742 
743         // protocol is wagon protocol but in fact dav is http(s)
744         if ( protocol.equals( "dav" ) )
745         {
746             protocol = "http";
747         }
748 
749         if ( protocol.equals( "davs" ) )
750         {
751             protocol = "https";
752         }
753 
754         String redirectUrl = protocol + "://localhost:" + getLocalPort( realServer );
755 
756         RedirectHandler redirectHandler =
757             new RedirectHandler( "See Other", HttpServletResponse.SC_SEE_OTHER, redirectUrl, null );
758 
759         redirectServer.setHandler( redirectHandler );
760 
761         redirectServer.start();
762 
763         wagon.connect( new Repository( "id", getRepositoryUrl( redirectServer ) ) );
764 
765         File tmpResult = File.createTempFile( "foo", "get" );
766 
767         try ( FileOutputStream fileOutputStream = new FileOutputStream( tmpResult ) )
768         {
769             wagon.getToStream( "resource", fileOutputStream );
770             fileOutputStream.flush();
771             fileOutputStream.close();
772             String found = FileUtils.fileRead( tmpResult );
773             assertEquals( "found:'" + found + "'", "Hello, World!", found );
774 
775             checkHandlerResult( redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER );
776             checkHandlerResult( handler.handlerRequestResponses, HttpServletResponse.SC_OK );
777         }
778         finally
779         {
780             wagon.disconnect();
781 
782             redirectServer.stop();
783             realServer.stop();
784 
785             tmpResult.delete();
786         }
787     }
788 
789     public void testRedirectGet()
790         throws Exception
791     {
792         StreamingWagon wagon = (StreamingWagon) getWagon();
793 
794         Server realServer = new Server( );
795         TestHeaderHandler handler = new TestHeaderHandler();
796 
797         realServer.setHandler( handler );
798         addConnector( realServer );
799         realServer.start();
800 
801         Server redirectServer = new Server( );
802 
803         addConnector( redirectServer );
804 
805         String protocol = getProtocol();
806 
807         // protocol is wagon protocol but in fact dav is http(s)
808         if ( protocol.equals( "dav" ) )
809         {
810             protocol = "http";
811         }
812 
813         if ( protocol.equals( "davs" ) )
814         {
815             protocol = "https";
816         }
817 
818         String redirectUrl = protocol + "://localhost:" + getLocalPort( realServer );
819 
820         RedirectHandler redirectHandler =
821             new RedirectHandler( "See Other", HttpServletResponse.SC_SEE_OTHER, redirectUrl, null );
822 
823         redirectServer.setHandler( redirectHandler );
824 
825         redirectServer.start();
826 
827         wagon.connect( new Repository( "id", getRepositoryUrl( redirectServer ) ) );
828 
829         File tmpResult = File.createTempFile( "foo", "get" );
830 
831         try
832         {
833             wagon.get( "resource", tmpResult );
834             String found = FileUtils.fileRead( tmpResult );
835             assertEquals( "found:'" + found + "'", "Hello, World!", found );
836 
837             checkHandlerResult( redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER );
838             checkHandlerResult( handler.handlerRequestResponses, HttpServletResponse.SC_OK );
839         }
840         finally
841         {
842             wagon.disconnect();
843 
844             redirectServer.stop();
845             realServer.stop();
846 
847             tmpResult.delete();
848         }
849     }
850 
851 
852     public void testRedirectPutFromStreamWithFullUrl()
853         throws Exception
854     {
855         Server realServer = new Server( );
856 
857         addConnector( realServer );
858 
859         File repositoryDirectory = getRepositoryDirectory();
860         FileUtils.deleteDirectory( repositoryDirectory );
861         repositoryDirectory.mkdirs();
862 
863         PutHandler putHandler = new PutHandler( repositoryDirectory );
864 
865         realServer.setHandler( putHandler );
866 
867         realServer.start();
868 
869         Server redirectServer = new Server( );
870 
871         addConnector( redirectServer );
872 
873         String protocol = getProtocol();
874 
875         // protocol is wagon protocol but in fact dav is http(s)
876         if ( protocol.equals( "dav" ) )
877         {
878             protocol = "http";
879         }
880 
881         if ( protocol.equals( "davs" ) )
882         {
883             protocol = "https";
884         }
885 
886         String redirectUrl = protocol + "://localhost:" + getLocalPort( realServer );
887 
888         RedirectHandler redirectHandler =
889             new RedirectHandler( "See Other", HttpServletResponse.SC_SEE_OTHER, redirectUrl, repositoryDirectory );
890 
891         redirectServer.setHandler( redirectHandler );
892 
893         redirectServer.start();
894 
895         try
896         {
897             StreamingWagon wagon = (StreamingWagon) getWagon();
898             Repository repository = new Repository( "foo", getRepositoryUrl( redirectServer ) );
899             wagon.connect( repository );
900 
901             File sourceFile = new File( repositoryDirectory, "test-secured-put-resource" );
902             sourceFile.delete();
903             assertFalse( sourceFile.exists() );
904 
905             File tempFile = File.createTempFile( "wagon", "tmp" );
906             tempFile.deleteOnExit();
907             String content = "put top secret";
908             FileUtils.fileWrite( tempFile.getAbsolutePath(), content );
909 
910             try ( FileInputStream fileInputStream = new FileInputStream( tempFile ) )
911             {
912                 wagon.putFromStream( fileInputStream, "test-secured-put-resource", content.length(), -1 );
913                 assertEquals( content, FileUtils.fileRead( sourceFile.getAbsolutePath() ) );
914 
915                 checkRequestResponseForRedirectPutWithFullUrl( redirectHandler, putHandler );
916             }
917             finally
918             {
919                 wagon.disconnect();
920                 tempFile.delete();
921             }
922 
923         }
924         finally
925         {
926             realServer.stop();
927             redirectServer.stop();
928         }
929     }
930 
931     protected void checkRequestResponseForRedirectPutWithFullUrl( RedirectHandler redirectHandler,
932                                                                   PutHandler putHandler )
933     {
934         checkHandlerResult( redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER );
935         checkHandlerResult( putHandler.handlerRequestResponses, HttpServletResponse.SC_CREATED );
936     }
937 
938     public void testRedirectPutFromStreamRelativeUrl()
939         throws Exception
940     {
941         Server realServer = new Server( );
942         addConnector( realServer );
943         File repositoryDirectory = getRepositoryDirectory();
944         FileUtils.deleteDirectory( repositoryDirectory );
945         repositoryDirectory.mkdirs();
946 
947         PutHandler putHandler = new PutHandler( repositoryDirectory );
948 
949         realServer.setHandler( putHandler );
950 
951         realServer.start();
952 
953         Server redirectServer = new Server( );
954 
955         addConnector( redirectServer );
956 
957         RedirectHandler redirectHandler =
958             new RedirectHandler( "See Other", HttpServletResponse.SC_SEE_OTHER, "/redirectRequest/foo",
959                                  repositoryDirectory );
960 
961         redirectServer.setHandler( redirectHandler );
962 
963         redirectServer.start();
964 
965         try
966         {
967             StreamingWagon wagon = (StreamingWagon) getWagon();
968             Repository repository = new Repository( "foo", getRepositoryUrl( redirectServer ) );
969             wagon.connect( repository );
970 
971             File sourceFile = new File( repositoryDirectory, "/redirectRequest/foo/test-secured-put-resource" );
972             sourceFile.delete();
973             assertFalse( sourceFile.exists() );
974 
975             File tempFile = File.createTempFile( "wagon", "tmp" );
976             tempFile.deleteOnExit();
977             String content = "put top secret";
978             FileUtils.fileWrite( tempFile.getAbsolutePath(), content );
979 
980             try ( FileInputStream fileInputStream = new FileInputStream( tempFile ) )
981             {
982                 wagon.putFromStream( fileInputStream, "test-secured-put-resource", content.length(), -1 );
983                 assertEquals( content, FileUtils.fileRead( sourceFile.getAbsolutePath() ) );
984 
985                 checkRequestResponseForRedirectPutWithRelativeUrl( redirectHandler, putHandler );
986             }
987             finally
988             {
989                 wagon.disconnect();
990                 tempFile.delete();
991             }
992 
993         }
994         finally
995         {
996             realServer.stop();
997             redirectServer.stop();
998         }
999     }
1000 
1001     protected void checkRequestResponseForRedirectPutWithRelativeUrl( RedirectHandler redirectHandler,
1002                                                                       PutHandler putHandler )
1003     {
1004         checkHandlerResult( redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER,
1005                             HttpServletResponse.SC_CREATED );
1006         checkHandlerResult( putHandler.handlerRequestResponses );
1007     }
1008 
1009     protected void checkHandlerResult( List<HandlerRequestResponse> handlerRequestResponses,
1010                                        int... expectedResponseCodes )
1011     {
1012         boolean success = true;
1013         if ( handlerRequestResponses.size() == expectedResponseCodes.length )
1014         {
1015             for ( int i = 0; i < expectedResponseCodes.length; i++ )
1016             {
1017                 success &= ( expectedResponseCodes[i] == handlerRequestResponses.get( i ).responseCode );
1018             }
1019         }
1020 
1021         if ( !success )
1022         {
1023             fail( "expected " + expectedResponseCodes + ", got " + handlerRequestResponses );
1024         }
1025     }
1026 
1027     public void testRedirectPutFileWithFullUrl()
1028         throws Exception
1029     {
1030         Server realServer = new Server( );
1031 
1032         addConnector( realServer );
1033 
1034         File repositoryDirectory = getRepositoryDirectory();
1035         FileUtils.deleteDirectory( repositoryDirectory );
1036         repositoryDirectory.mkdirs();
1037 
1038         PutHandler putHandler = new PutHandler( repositoryDirectory );
1039 
1040         realServer.setHandler( putHandler );
1041 
1042         realServer.start();
1043 
1044         Server redirectServer = new Server( );
1045 
1046         addConnector( redirectServer );
1047 
1048         String protocol = getProtocol();
1049 
1050         // protocol is wagon protocol but in fact dav is http(s)
1051         if ( protocol.equals( "dav" ) )
1052         {
1053             protocol = "http";
1054         }
1055 
1056         if ( protocol.equals( "davs" ) )
1057         {
1058             protocol = "https";
1059         }
1060 
1061         String redirectUrl = protocol + "://localhost:" + getLocalPort( realServer );
1062 
1063         RedirectHandler redirectHandler =
1064             new RedirectHandler( "See Other", HttpServletResponse.SC_SEE_OTHER, redirectUrl, repositoryDirectory );
1065 
1066         redirectServer.setHandler( redirectHandler );
1067 
1068         redirectServer.start();
1069 
1070         try
1071         {
1072             StreamingWagon wagon = (StreamingWagon) getWagon();
1073             Repository repository = new Repository( "foo", getRepositoryUrl( redirectServer ) );
1074             wagon.connect( repository );
1075 
1076             File sourceFile = new File( repositoryDirectory, "test-secured-put-resource" );
1077             sourceFile.delete();
1078             assertFalse( sourceFile.exists() );
1079 
1080             File tempFile = File.createTempFile( "wagon", "tmp" );
1081             tempFile.deleteOnExit();
1082             String content = "put top secret";
1083             FileUtils.fileWrite( tempFile.getAbsolutePath(), content );
1084 
1085             try
1086             {
1087                 wagon.put( tempFile, "test-secured-put-resource" );
1088                 assertEquals( content, FileUtils.fileRead( sourceFile.getAbsolutePath() ) );
1089 
1090                 checkRequestResponseForRedirectPutWithFullUrl( redirectHandler, putHandler );
1091             }
1092             finally
1093             {
1094                 wagon.disconnect();
1095                 tempFile.delete();
1096             }
1097 
1098         }
1099         finally
1100         {
1101             realServer.stop();
1102             redirectServer.stop();
1103         }
1104     }
1105 
1106 
1107     public void testRedirectPutFileRelativeUrl()
1108         throws Exception
1109     {
1110         Server realServer = new Server( );
1111         addConnector( realServer );
1112         File repositoryDirectory = getRepositoryDirectory();
1113         FileUtils.deleteDirectory( repositoryDirectory );
1114         repositoryDirectory.mkdirs();
1115 
1116         PutHandler putHandler = new PutHandler( repositoryDirectory );
1117 
1118         realServer.setHandler( putHandler );
1119 
1120         realServer.start();
1121 
1122         Server redirectServer = new Server( );
1123 
1124         addConnector( redirectServer );
1125 
1126         RedirectHandler redirectHandler =
1127             new RedirectHandler( "See Other", HttpServletResponse.SC_SEE_OTHER, "/redirectRequest/foo",
1128                                  repositoryDirectory );
1129 
1130         redirectServer.setHandler( redirectHandler );
1131 
1132         redirectServer.start();
1133 
1134         try
1135         {
1136             StreamingWagon wagon = (StreamingWagon) getWagon();
1137             Repository repository = new Repository( "foo", getRepositoryUrl( redirectServer ) );
1138             wagon.connect( repository );
1139 
1140             File sourceFile = new File( repositoryDirectory, "/redirectRequest/foo/test-secured-put-resource" );
1141             sourceFile.delete();
1142             assertFalse( sourceFile.exists() );
1143 
1144             File tempFile = File.createTempFile( "wagon", "tmp" );
1145             tempFile.deleteOnExit();
1146             String content = "put top secret";
1147             FileUtils.fileWrite( tempFile.getAbsolutePath(), content );
1148 
1149             try
1150             {
1151                 wagon.put( tempFile, "test-secured-put-resource" );
1152                 assertEquals( content, FileUtils.fileRead( sourceFile.getAbsolutePath() ) );
1153 
1154                 checkRequestResponseForRedirectPutWithRelativeUrl( redirectHandler, putHandler );
1155             }
1156             finally
1157             {
1158                 wagon.disconnect();
1159                 tempFile.delete();
1160             }
1161 
1162         }
1163         finally
1164         {
1165             realServer.stop();
1166             redirectServer.stop();
1167         }
1168     }
1169 
1170 
1171     /**
1172      *
1173      */
1174     @SuppressWarnings( "checkstyle:visibilitymodifier" )
1175     public static class RedirectHandler
1176         extends AbstractHandler
1177     {
1178         String reason;
1179 
1180         int retCode;
1181 
1182         String redirectUrl;
1183 
1184         File repositoryDirectory;
1185 
1186         public List<HandlerRequestResponse> handlerRequestResponses = new ArrayList<HandlerRequestResponse>();
1187 
1188         RedirectHandler( String reason, int retCode, String redirectUrl, File repositoryDirectory )
1189         {
1190             this.reason = reason;
1191             this.retCode = retCode;
1192             this.redirectUrl = redirectUrl;
1193             this.repositoryDirectory = repositoryDirectory;
1194         }
1195 
1196         public void handle( String target, Request baseRequest, HttpServletRequest request,
1197             HttpServletResponse response ) throws IOException, ServletException
1198         {
1199             if ( request.getRequestURI().contains( "redirectRequest" ) )
1200             {
1201                 PutHandler putHandler = new PutHandler( this.repositoryDirectory );
1202                 putHandler.handle( target, baseRequest, request, response );
1203                 handlerRequestResponses.add(
1204                     new HandlerRequestResponse( request.getMethod(), response.getStatus(),
1205                                                 request.getRequestURI() ) );
1206                 return;
1207             }
1208             response.setStatus( this.retCode );
1209             response.setHeader( "Location", this.redirectUrl + request.getRequestURI() );
1210             baseRequest.setHandled( true );
1211 
1212             handlerRequestResponses.add(
1213                 new HandlerRequestResponse( request.getMethod(), response.getStatus(),
1214                                             request.getRequestURI() ) );
1215         }
1216 
1217 
1218     }
1219 
1220 
1221     private void runTestProxiedRequest( ProxyInfo proxyInfo, TestHeaderHandler handler )
1222         throws Exception
1223     {
1224         // what an UGLY hack!
1225         // but apparently jetty needs some time to free up resources
1226         // <5s: broken test :(
1227         // CHECKSTYLE_OFF: MagicNumber
1228         Thread.sleep( 5001L );
1229         // CHECKSTYLE_ON: MagicNumber
1230 
1231         Server proxyServer = new Server( );
1232         ServerConnector serverConnector =
1233             new ServerConnector( proxyServer, new HttpConnectionFactory( new HttpConfiguration() ) );
1234         proxyServer.addConnector( serverConnector );
1235         proxyServer.setHandler( handler );
1236 
1237         proxyServer.start();
1238 
1239         proxyInfo.setPort( getLocalPort( proxyServer ) );
1240 
1241         System.out.println(
1242             "start proxy on host/port " + proxyInfo.getHost() + "/" + proxyInfo.getPort() + " with non proxyHosts "
1243                 + proxyInfo.getNonProxyHosts() );
1244 
1245         while ( !proxyServer.isRunning() || !proxyServer.isStarted() )
1246         {
1247             Thread.sleep( 10 );
1248         }
1249 
1250         try
1251         {
1252             StreamingWagon wagon = (StreamingWagon) getWagon();
1253 
1254             Repository testRepository = new Repository( "id", "http://www.example.com/" );
1255 
1256             String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1257             File sourceFile = new File( localRepositoryPath, "test-proxied-resource" );
1258             FileUtils.fileWrite( sourceFile.getAbsolutePath(), "content" );
1259 
1260             wagon.connect( testRepository, proxyInfo );
1261 
1262             try
1263             {
1264                 wagon.getToStream( "test-proxied-resource", new ByteArrayOutputStream() );
1265 
1266                 assertTrue( handler.headers.containsKey( "Proxy-Connection" ) );
1267             }
1268             finally
1269             {
1270                 System.setProperty( "http.proxyHost", "" );
1271                 System.setProperty( "http.proxyPort", "" );
1272                 wagon.disconnect();
1273             }
1274         }
1275         finally
1276         {
1277             proxyServer.stop();
1278         }
1279     }
1280 
1281     private void runTestProxiedRequestWithProvider( ProxyInfoProvider proxyInfoProvider, TestHeaderHandler handler )
1282         throws Exception
1283     {
1284         // what an UGLY hack!
1285         // but apparently jetty needs some time to free up resources
1286         // <5s: broken test :(
1287         // CHECKSTYLE_OFF: MagicNumber
1288         Thread.sleep( 5001L );
1289         // CHECKSTYLE_ON: MagicNumber
1290 
1291         Server proxyServer = new Server( );
1292         ServerConnector serverConnector =
1293             new ServerConnector( proxyServer, new HttpConnectionFactory( new HttpConfiguration() ) );
1294         proxyServer.addConnector( serverConnector );
1295 
1296         proxyServer.setHandler( handler );
1297 
1298         proxyServer.start();
1299 
1300         proxyInfoProvider.getProxyInfo( null ).setPort( getLocalPort( proxyServer ) );
1301 
1302         System.out.println( "start proxy on host/port " + proxyInfoProvider.getProxyInfo( null ).getHost() + "/"
1303                                 + proxyInfoProvider.getProxyInfo( null ).getPort() + " with non proxyHosts "
1304                                 + proxyInfoProvider.getProxyInfo( null ).getNonProxyHosts() );
1305 
1306         while ( !proxyServer.isRunning() || !proxyServer.isStarted() )
1307         {
1308             Thread.sleep( 10 );
1309         }
1310 
1311         try
1312         {
1313             StreamingWagon wagon = (StreamingWagon) getWagon();
1314 
1315             Repository testRepository = new Repository( "id", "http://www.example.com/" );
1316 
1317             String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1318             File sourceFile = new File( localRepositoryPath, "test-proxied-resource" );
1319             FileUtils.fileWrite( sourceFile.getAbsolutePath(), "content" );
1320 
1321             wagon.connect( testRepository, proxyInfoProvider );
1322 
1323             try
1324             {
1325                 wagon.getToStream( "test-proxied-resource", new ByteArrayOutputStream() );
1326 
1327                 assertTrue( handler.headers.containsKey( "Proxy-Connection" ) );
1328             }
1329             finally
1330             {
1331                 System.setProperty( "http.proxyHost", "" );
1332                 System.setProperty( "http.proxyPort", "" );
1333                 wagon.disconnect();
1334             }
1335         }
1336         finally
1337         {
1338             proxyServer.stop();
1339         }
1340     }
1341 
1342     private ProxyInfo createProxyInfo()
1343     {
1344         ProxyInfo proxyInfo = new ProxyInfo();
1345         proxyInfo.setHost( "localhost" );
1346         proxyInfo.setNonProxyHosts( null );
1347         proxyInfo.setType( "http" );
1348         return proxyInfo;
1349     }
1350 
1351     public void testSecuredGetUnauthorized()
1352         throws Exception
1353     {
1354         try
1355         {
1356             runTestSecuredGet( null );
1357             fail();
1358         }
1359         catch ( AuthorizationException e )
1360         {
1361             assertTrue( true );
1362         }
1363     }
1364 
1365     public void testSecuredGetWrongPassword()
1366         throws Exception
1367     {
1368         try
1369         {
1370             AuthenticationInfo authInfo = new AuthenticationInfo();
1371             authInfo.setUserName( "user" );
1372             authInfo.setPassword( "admin" );
1373             runTestSecuredGet( authInfo );
1374             fail();
1375         }
1376         catch ( AuthorizationException e )
1377         {
1378             assertTrue( true );
1379         }
1380     }
1381 
1382     public void testSecuredGet()
1383         throws Exception
1384     {
1385         AuthenticationInfo authInfo = new AuthenticationInfo();
1386         authInfo.setUserName( "user" );
1387         authInfo.setPassword( "secret" );
1388         runTestSecuredGet( authInfo );
1389     }
1390 
1391 
1392     public void runTestSecuredGet( AuthenticationInfo authInfo )
1393         throws Exception
1394     {
1395         String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1396         Server server = createSecurityServer( localRepositoryPath );
1397 
1398         server.start();
1399 
1400         try
1401         {
1402             StreamingWagon wagon = (StreamingWagon) getWagon();
1403 
1404             Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
1405 
1406             File sourceFile = new File( localRepositoryPath, "test-secured-resource" );
1407             FileUtils.fileWrite( sourceFile.getAbsolutePath(), "top secret" );
1408 
1409             wagon.connect( testRepository, authInfo );
1410 
1411             File file = File.createTempFile( "wagon-test", "txt" );
1412 
1413             try
1414             {
1415                 wagon.get( "test-secured-resource", file );
1416             }
1417             finally
1418             {
1419                 wagon.disconnect();
1420             }
1421 
1422             FileInputStream in = new FileInputStream( file );
1423 
1424             assertEquals( "top secret", IOUtil.toString( in ) );
1425 
1426             /*
1427              * We need to wait a bit for all Jetty workers/threads to complete their work. Otherwise
1428              * we may suffer from race conditions where handlerRequestResponses list is not completely
1429              * populated and its premature iteration in testPreemptiveAuthenticationGet will lead to
1430              * a test failure.
1431              */
1432             // CHECKSTYLE_OFF: MagicNumber
1433             Thread.sleep ( 2000L );
1434             // CHECKSTYLE_ON: MagicNumber
1435 
1436             TestSecurityHandler securityHandler = server.getChildHandlerByClass( TestSecurityHandler.class );
1437             testPreemptiveAuthenticationGet( securityHandler, supportPreemptiveAuthenticationGet() );
1438 
1439         }
1440         finally
1441         {
1442             server.stop();
1443         }
1444     }
1445 
1446 
1447     public void testSecuredGetToStream()
1448         throws Exception
1449     {
1450         AuthenticationInfo authInfo = new AuthenticationInfo();
1451         authInfo.setUserName( "user" );
1452         authInfo.setPassword( "secret" );
1453         runTestSecuredGetToStream( authInfo );
1454     }
1455 
1456     public void runTestSecuredGetToStream( AuthenticationInfo authInfo )
1457         throws Exception
1458     {
1459         String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1460         Server server = createSecurityServer( localRepositoryPath );
1461 
1462         server.start();
1463 
1464         try
1465         {
1466             StreamingWagon wagon = (StreamingWagon) getWagon();
1467 
1468             Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
1469 
1470             File sourceFile = new File( localRepositoryPath, "test-secured-resource" );
1471             FileUtils.fileWrite( sourceFile.getAbsolutePath(), "top secret" );
1472 
1473             wagon.connect( testRepository, authInfo );
1474 
1475             ByteArrayOutputStream out = new ByteArrayOutputStream();
1476             try
1477             {
1478                 wagon.getToStream( "test-secured-resource", out );
1479             }
1480             finally
1481             {
1482                 wagon.disconnect();
1483             }
1484 
1485             assertEquals( "top secret", out.toString( "US-ASCII" ) );
1486 
1487             /*
1488              * We need to wait a bit for all Jetty workers/threads to complete their work. Otherwise
1489              * we may suffer from race conditions where handlerRequestResponses list is not completely
1490              * populated and its premature iteration in testPreemptiveAuthenticationGet will lead to
1491              * a test failure.
1492              */
1493             // CHECKSTYLE_OFF: MagicNumber
1494             Thread.sleep ( 2000L );
1495             // CHECKSTYLE_ON: MagicNumber
1496 
1497             TestSecurityHandler securityHandler = server.getChildHandlerByClass( TestSecurityHandler.class );
1498             testPreemptiveAuthenticationGet( securityHandler, supportPreemptiveAuthenticationGet() );
1499         }
1500         finally
1501         {
1502             server.stop();
1503         }
1504     }
1505 
1506     public void testSecuredResourceExistsUnauthorized()
1507         throws Exception
1508     {
1509         try
1510         {
1511             runTestSecuredResourceExists( null );
1512             fail();
1513         }
1514         catch ( AuthorizationException e )
1515         {
1516             assertTrue( true );
1517         }
1518     }
1519 
1520     public void testSecuredResourceExistsWrongPassword()
1521         throws Exception
1522     {
1523         try
1524         {
1525             AuthenticationInfo authInfo = new AuthenticationInfo();
1526             authInfo.setUserName( "user" );
1527             authInfo.setPassword( "admin" );
1528             runTestSecuredResourceExists( authInfo );
1529         }
1530         catch ( AuthorizationException e )
1531         {
1532             assertTrue( true );
1533         }
1534     }
1535 
1536     public void testSecuredResourceExists()
1537         throws Exception
1538     {
1539         AuthenticationInfo authInfo = new AuthenticationInfo();
1540         authInfo.setUserName( "user" );
1541         authInfo.setPassword( "secret" );
1542         runTestSecuredResourceExists( authInfo );
1543     }
1544 
1545     public void runTestSecuredResourceExists( AuthenticationInfo authInfo )
1546         throws Exception
1547     {
1548         String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1549         Server server = createSecurityServer( localRepositoryPath );
1550 
1551         server.start();
1552 
1553         try
1554         {
1555             StreamingWagon wagon = (StreamingWagon) getWagon();
1556 
1557             Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
1558 
1559             File sourceFile = new File( localRepositoryPath, "test-secured-resource-exists" );
1560             FileUtils.fileWrite( sourceFile.getAbsolutePath(), "top secret" );
1561 
1562             wagon.connect( testRepository, authInfo );
1563 
1564             try
1565             {
1566                 assertTrue( wagon.resourceExists( "test-secured-resource-exists" ) );
1567 
1568                 assertFalse( wagon.resourceExists( "test-secured-resource-not-exists" ) );
1569             }
1570             finally
1571             {
1572                 wagon.disconnect();
1573             }
1574         }
1575         finally
1576         {
1577             server.stop();
1578         }
1579     }
1580 
1581     private Server createSecurityServer( String localRepositoryPath )
1582     {
1583         Server server = new Server( );
1584 
1585         SecurityHandler sh = createSecurityHandler();
1586 
1587         ServletContextHandler root = new ServletContextHandler( ServletContextHandler.SESSIONS
1588             | ServletContextHandler.SECURITY );
1589         root.setResourceBase( localRepositoryPath );
1590         root.setSecurityHandler( sh );
1591         ServletHolder servletHolder = new ServletHolder( new DefaultServlet() );
1592         root.addServlet( servletHolder, "/*" );
1593 
1594         server.setHandler( root );
1595         addConnector( server );
1596         return server;
1597     }
1598 
1599     private Server createStatusServer( int status )
1600     {
1601         Server server = new Server( );
1602         StatusHandler handler = new StatusHandler();
1603         handler.setStatusToReturn( status );
1604         server.setHandler( handler );
1605         addConnector( server );
1606         return server;
1607     }
1608 
1609 
1610     private String writeTestFile( File parent, String child, String compressionType )
1611         throws IOException
1612     {
1613         File file = new File( parent, child );
1614         file.getParentFile().mkdirs();
1615         file.deleteOnExit();
1616         OutputStream out = new FileOutputStream( file );
1617         try
1618         {
1619             out.write( child.getBytes() );
1620         }
1621         finally
1622         {
1623             out.close();
1624         }
1625 
1626         String ext = "";
1627         if ( "gzip".equals( compressionType ) )
1628         {
1629             ext = ".gz";
1630         }
1631         if ( "deflate".equals( compressionType ) )
1632         {
1633             ext = ".deflate";
1634         }
1635 
1636         file = new File( parent, child + ext );
1637         file.deleteOnExit();
1638         String content;
1639         out = new FileOutputStream( file );
1640         if ( "gzip".equals( compressionType ) )
1641         {
1642             out = new GZIPOutputStream( out );
1643         }
1644         if ( "deflate".equals( compressionType ) )
1645         {
1646             out = new DeflaterOutputStream( out );
1647         }
1648         try
1649         {
1650             // write out different data than non-compressed file, so we can
1651             // assert the compressed version was returned
1652             content = file.getAbsolutePath();
1653             out.write( content.getBytes() );
1654         }
1655         finally
1656         {
1657             out.close();
1658         }
1659 
1660         return content;
1661     }
1662 
1663     public void testPutForbidden()
1664         throws Exception
1665     {
1666         try
1667         {
1668             runTestPut( HttpServletResponse.SC_FORBIDDEN );
1669             fail();
1670         }
1671         catch ( AuthorizationException e )
1672         {
1673             assertTrue( true );
1674         }
1675     }
1676 
1677     public void testPut404()
1678         throws Exception
1679     {
1680         try
1681         {
1682             runTestPut( HttpServletResponse.SC_NOT_FOUND );
1683             fail();
1684         }
1685         catch ( ResourceDoesNotExistException e )
1686         {
1687             assertTrue( true );
1688         }
1689     }
1690 
1691     public void testPut500()
1692         throws Exception
1693     {
1694         try
1695         {
1696             runTestPut( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
1697             fail();
1698         }
1699         catch ( TransferFailedException e )
1700         {
1701             assertTrue( true );
1702         }
1703     }
1704 
1705     public void testPut429()
1706         throws Exception
1707     {
1708 
1709         try
1710         {
1711 
1712             StreamingWagon wagon = (StreamingWagon) getWagon();
1713             Server server = new Server( );
1714             final AtomicBoolean called = new AtomicBoolean();
1715 
1716             AbstractHandler handler = new AbstractHandler()
1717             {
1718                 public void handle( String target, Request baseRequest, HttpServletRequest request,
1719                     HttpServletResponse response ) throws IOException, ServletException
1720                 {
1721                     if ( called.get() )
1722                     {
1723                         response.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR );
1724                         baseRequest.setHandled( true );
1725                     }
1726                     else
1727                     {
1728                         called.set( true );
1729                         response.setStatus( SC_TOO_MANY_REQUESTS );
1730                         baseRequest.setHandled( true );
1731                     }
1732                 }
1733             };
1734 
1735             server.setHandler( handler );
1736             addConnector( server );
1737             server.start();
1738 
1739             wagon.connect( new Repository( "id", getRepositoryUrl( server ) ) );
1740 
1741             File tempFile = File.createTempFile( "wagon", "tmp" );
1742             tempFile.deleteOnExit();
1743             FileUtils.fileWrite( tempFile.getAbsolutePath(), "content" );
1744 
1745             try
1746             {
1747                 wagon.put( tempFile, "resource" );
1748                 fail();
1749             }
1750             finally
1751             {
1752                 wagon.disconnect();
1753 
1754                 server.stop();
1755 
1756                 tempFile.delete();
1757             }
1758 
1759         }
1760         catch ( TransferFailedException e )
1761         {
1762             assertTrue( true );
1763         }
1764     }
1765 
1766 
1767     private void runTestPut( int status )
1768         throws Exception
1769     {
1770         StreamingWagon wagon = (StreamingWagon) getWagon();
1771 
1772         Server server = createStatusServer( status );
1773         server.start();
1774 
1775         wagon.connect( new Repository( "id", getRepositoryUrl( server ) ) );
1776 
1777         File tempFile = File.createTempFile( "wagon", "tmp" );
1778         tempFile.deleteOnExit();
1779         FileUtils.fileWrite( tempFile.getAbsolutePath(), "content" );
1780 
1781         String baseUrl = getRepositoryUrl( server );
1782         String resourceName = "resource";
1783         String serverReasonPhrase = HttpStatus.getCode( status ).getMessage();
1784 
1785         try
1786         {
1787             wagon.put( tempFile, resourceName );
1788             fail();
1789         }
1790         catch ( Exception e )
1791         {
1792             verifyWagonExceptionMessage( e, status, baseUrl + "/" + resourceName, serverReasonPhrase );
1793             throw e;
1794         }
1795         finally
1796         {
1797             wagon.disconnect();
1798 
1799             server.stop();
1800 
1801             tempFile.delete();
1802         }
1803     }
1804 
1805     public void testSecuredPutUnauthorized()
1806         throws Exception
1807     {
1808         try
1809         {
1810             runTestSecuredPut( null );
1811             fail();
1812         }
1813         catch ( TransferFailedException e )
1814         {
1815             assertTrue( true );
1816         }
1817     }
1818 
1819     public void testSecuredPutWrongPassword()
1820         throws Exception
1821     {
1822         try
1823         {
1824             AuthenticationInfo authInfo = new AuthenticationInfo();
1825             authInfo.setUserName( "user" );
1826             authInfo.setPassword( "admin" );
1827             runTestSecuredPut( authInfo );
1828             fail();
1829         }
1830         catch ( TransferFailedException e )
1831         {
1832             assertTrue( true );
1833         }
1834     }
1835 
1836     public void testSecuredPut()
1837         throws Exception
1838     {
1839         AuthenticationInfo authInfo = new AuthenticationInfo();
1840         authInfo.setUserName( "user" );
1841         authInfo.setPassword( "secret" );
1842         runTestSecuredPut( authInfo );
1843     }
1844 
1845     public void runTestSecuredPut( AuthenticationInfo authInfo )
1846         throws Exception
1847     {
1848         runTestSecuredPut( authInfo, 1 );
1849     }
1850 
1851     public void runTestSecuredPut( AuthenticationInfo authInfo, int putNumber )
1852         throws Exception
1853     {
1854         String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1855         Server server = new Server( );
1856 
1857         TestSecurityHandler sh = createSecurityHandler();
1858 
1859         PutHandler putHandler = new PutHandler( new File( localRepositoryPath ) );
1860 
1861         sh.setHandler( putHandler );
1862         server.setHandler( sh );
1863         addConnector( server );
1864         server.start();
1865 
1866         StreamingWagon wagon = (StreamingWagon) getWagon();
1867         Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
1868         wagon.connect( testRepository, authInfo );
1869         try
1870         {
1871             for ( int i = 0; i < putNumber; i++ )
1872             {
1873                 File sourceFile = new File( localRepositoryPath, "test-secured-put-resource" );
1874                 sourceFile.delete();
1875                 assertFalse( sourceFile.exists() );
1876 
1877                 File tempFile = File.createTempFile( "wagon", "tmp" );
1878                 tempFile.deleteOnExit();
1879                 FileUtils.fileWrite( tempFile.getAbsolutePath(), "put top secret" );
1880 
1881                 try
1882                 {
1883                     wagon.put( tempFile, "test-secured-put-resource" );
1884                 }
1885                 finally
1886                 {
1887                     tempFile.delete();
1888                 }
1889 
1890                 assertEquals( "put top secret", FileUtils.fileRead( sourceFile.getAbsolutePath() ) );
1891             }
1892         }
1893         finally
1894         {
1895             wagon.disconnect();
1896             server.stop();
1897         }
1898         assertEquals( putNumber, putHandler.putCallNumber );
1899         testPreemptiveAuthenticationPut( sh, supportPreemptiveAuthenticationPut() );
1900     }
1901 
1902     public void testNonSecuredPutFromStream()
1903         throws Exception
1904     {
1905         AuthenticationInfo authInfo = new AuthenticationInfo();
1906         authInfo.setUserName( "user" );
1907         authInfo.setPassword( "secret" );
1908         runTestSecuredPutFromStream( authInfo, 1, false );
1909     }
1910 
1911     public void testSecuredPutFromStream()
1912         throws Exception
1913     {
1914         AuthenticationInfo authInfo = new AuthenticationInfo();
1915         authInfo.setUserName( "user" );
1916         authInfo.setPassword( "secret" );
1917         runTestSecuredPutFromStream( authInfo, 1, true );
1918     }
1919 
1920     public void runTestSecuredPutFromStream( AuthenticationInfo authInfo, int putNumber, boolean addSecurityHandler )
1921         throws Exception
1922     {
1923         String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
1924         Server server = new Server( );
1925 
1926         TestSecurityHandler sh = createSecurityHandler();
1927 
1928         PutHandler putHandler = new PutHandler( new File( localRepositoryPath ) );
1929 
1930         if ( addSecurityHandler )
1931         {
1932             sh.setHandler( putHandler );
1933             server.setHandler( sh );
1934         }
1935         else
1936         {
1937             server.setHandler( putHandler );
1938         }
1939         addConnector( server );
1940         server.start();
1941 
1942         StreamingWagon wagon = (StreamingWagon) getWagon();
1943         Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
1944         if ( addSecurityHandler )
1945         {
1946             wagon.connect( testRepository, authInfo );
1947         }
1948         else
1949         {
1950             wagon.connect( testRepository );
1951         }
1952         try
1953         {
1954             for ( int i = 0; i < putNumber; i++ )
1955             {
1956                 File sourceFile = new File( localRepositoryPath, "test-secured-put-resource" );
1957                 sourceFile.delete();
1958                 assertFalse( sourceFile.exists() );
1959 
1960                 File tempFile = File.createTempFile( "wagon", "tmp" );
1961                 tempFile.deleteOnExit();
1962                 String content = "put top secret";
1963                 FileUtils.fileWrite( tempFile.getAbsolutePath(), content );
1964 
1965                 try ( FileInputStream fileInputStream = new FileInputStream( tempFile ) )
1966                 {
1967                     wagon.putFromStream( fileInputStream, "test-secured-put-resource", content.length(), -1 );
1968                 }
1969                 finally
1970                 {
1971                     tempFile.delete();
1972                 }
1973 
1974                 assertEquals( content, FileUtils.fileRead( sourceFile.getAbsolutePath() ) );
1975             }
1976         }
1977         finally
1978         {
1979             wagon.disconnect();
1980             server.stop();
1981         }
1982         assertEquals( putNumber, putHandler.putCallNumber );
1983         if ( addSecurityHandler )
1984         {
1985             testPreemptiveAuthenticationPut( sh, supportPreemptiveAuthenticationPut() );
1986         }
1987 
1988         // ensure we didn't use chunked transfer which doesn't work on ngnix
1989         for ( DeployedResource deployedResource : putHandler.deployedResources )
1990         {
1991             if ( StringUtils.equalsIgnoreCase( "chunked", deployedResource.transferEncoding ) )
1992             {
1993                 fail( "deployedResource use chunked: " + deployedResource );
1994             }
1995         }
1996     }
1997 
1998 
1999     protected abstract boolean supportPreemptiveAuthenticationPut();
2000 
2001     protected abstract boolean supportPreemptiveAuthenticationGet();
2002 
2003     protected abstract boolean supportProxyPreemptiveAuthentication();
2004 
2005     protected void testPreemptiveAuthenticationGet( TestSecurityHandler sh, boolean preemptive )
2006     {
2007         testPreemptiveAuthentication( sh, preemptive, HttpServletResponse.SC_OK );
2008     }
2009 
2010     protected void testPreemptiveAuthenticationPut( TestSecurityHandler sh, boolean preemptive )
2011     {
2012         testPreemptiveAuthentication( sh, preemptive, HttpServletResponse.SC_CREATED );
2013     }
2014 
2015     protected void testPreemptiveAuthentication( TestSecurityHandler sh, boolean preemptive, int statusCode )
2016     {
2017 
2018         if ( preemptive )
2019         {
2020             assertEquals( "not 1 security handler use " + sh.handlerRequestResponses, 1,
2021                           sh.handlerRequestResponses.size() );
2022             assertEquals( statusCode, sh.handlerRequestResponses.get( 0 ).responseCode );
2023         }
2024         else
2025         {
2026             assertEquals( "not 2 security handler use " + sh.handlerRequestResponses, 2,
2027                           sh.handlerRequestResponses.size() );
2028             assertEquals( HttpServletResponse.SC_UNAUTHORIZED, sh.handlerRequestResponses.get( 0 ).responseCode );
2029             assertEquals( statusCode, sh.handlerRequestResponses.get( 1 ).responseCode );
2030 
2031         }
2032     }
2033 
2034     static class StatusHandler
2035         extends AbstractHandler
2036     {
2037         private int status;
2038 
2039         public void setStatusToReturn( int status )
2040         {
2041             this.status = status;
2042         }
2043 
2044         public void handle( String target, Request baseRequest, HttpServletRequest request,
2045             HttpServletResponse response ) throws IOException, ServletException
2046         {
2047             if ( status != 0 )
2048             {
2049                 response.setStatus( status );
2050                 baseRequest.setHandled( true );
2051             }
2052         }
2053     }
2054 
2055     static class DeployedResource
2056     {
2057         String httpMethod;
2058 
2059         String requestUri;
2060 
2061         String contentLength;
2062 
2063         String transferEncoding;
2064 
2065         DeployedResource()
2066         {
2067             // no op
2068         }
2069 
2070         @Override
2071         public String toString()
2072         {
2073             final StringBuilder sb = new StringBuilder();
2074             sb.append( "DeployedResource" );
2075             sb.append( "{httpMethod='" ).append( httpMethod ).append( '\'' );
2076             sb.append( ", requestUri='" ).append( requestUri ).append( '\'' );
2077             sb.append( ", contentLength='" ).append( contentLength ).append( '\'' );
2078             sb.append( ", transferEncoding='" ).append( transferEncoding ).append( '\'' );
2079             sb.append( '}' );
2080             return sb.toString();
2081         }
2082     }
2083 
2084     /**
2085      *
2086      */
2087     @SuppressWarnings( "checkstyle:visibilitymodifier" )
2088     public static class PutHandler
2089         extends AbstractHandler
2090     {
2091         private final File resourceBase;
2092 
2093         public List<DeployedResource> deployedResources = new ArrayList<DeployedResource>();
2094 
2095         public int putCallNumber = 0;
2096 
2097         public List<HandlerRequestResponse> handlerRequestResponses = new ArrayList<HandlerRequestResponse>();
2098 
2099         public PutHandler( File repositoryDirectory )
2100         {
2101             this.resourceBase = repositoryDirectory;
2102         }
2103 
2104         public void handle( String target, Request baseRequest, HttpServletRequest request,
2105             HttpServletResponse response ) throws IOException, ServletException
2106         {
2107             if ( baseRequest.isHandled() || !"PUT".equals( baseRequest.getMethod() ) )
2108             {
2109                 return;
2110             }
2111 
2112             baseRequest.setHandled( true );
2113 
2114             File file = new File( resourceBase, URLDecoder.decode( request.getPathInfo() ) );
2115             file.getParentFile().mkdirs();
2116             OutputStream out = null;
2117             InputStream in = null;
2118             try
2119             {
2120                 in = request.getInputStream();
2121                 out = new FileOutputStream( file );
2122                 IOUtil.copy( in, out );
2123                 out.close();
2124                 out = null;
2125                 in.close();
2126                 in = null;
2127             }
2128             finally
2129             {
2130                 IOUtil.close( in );
2131                 IOUtil.close( out );
2132             }
2133             putCallNumber++;
2134             DeployedResource deployedResource = new DeployedResource();
2135 
2136             deployedResource.httpMethod = request.getMethod();
2137             deployedResource.requestUri = request.getRequestURI();
2138             deployedResource.transferEncoding = request.getHeader( "Transfer-Encoding" );
2139             deployedResource.contentLength = request.getHeader( "Content-Length" );
2140             deployedResources.add( deployedResource );
2141 
2142             response.setStatus( HttpServletResponse.SC_CREATED );
2143 
2144             handlerRequestResponses.add(
2145                 new HandlerRequestResponse( request.getMethod(), ( (Response) response ).getStatus(),
2146                                             request.getRequestURI() ) );
2147         }
2148     }
2149 
2150     private static class AuthorizingProxyHandler
2151         extends TestHeaderHandler
2152     {
2153 
2154         List<HandlerRequestResponse> handlerRequestResponses = new ArrayList<HandlerRequestResponse>();
2155 
2156         public void handle( String target, Request baseRequest, HttpServletRequest request,
2157             HttpServletResponse response ) throws IOException, ServletException
2158         {
2159             System.out.println( " handle proxy request" );
2160             if ( request.getHeader( "Proxy-Authorization" ) == null )
2161             {
2162                 handlerRequestResponses.add(
2163                     new HandlerRequestResponse( request.getMethod(),
2164                                                 HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED,
2165                                                 request.getRequestURI() ) );
2166                 response.setStatus( HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED );
2167                 response.addHeader( "Proxy-Authenticate", "Basic realm=\"Squid proxy-caching web server\"" );
2168 
2169                 baseRequest.setHandled( true );
2170                 return;
2171             }
2172             handlerRequestResponses.add(
2173                 new HandlerRequestResponse( request.getMethod(), HttpServletResponse.SC_OK, request.getRequestURI() ) );
2174             super.handle( target, baseRequest, request, response );
2175         }
2176     }
2177 
2178     /**
2179      *
2180      */
2181     @SuppressWarnings( "checkstyle:visibilitymodifier" )
2182     private static class TestHeaderHandler
2183         extends AbstractHandler
2184     {
2185         public Map<String, String> headers = Collections.emptyMap();
2186 
2187         public List<HandlerRequestResponse> handlerRequestResponses = new ArrayList<HandlerRequestResponse>();
2188 
2189         TestHeaderHandler()
2190         {
2191         }
2192 
2193         public void handle( String target, Request baseRrequest, HttpServletRequest request,
2194             HttpServletResponse response ) throws IOException, ServletException
2195         {
2196             headers = new HashMap<String, String>();
2197             for ( Enumeration<String> e = baseRrequest.getHeaderNames(); e.hasMoreElements(); )
2198             {
2199                 String name = e.nextElement();
2200                 Enumeration headerValues = baseRrequest.getHeaders( name );
2201                 // as per HTTP spec http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
2202                 // multiple values for the same header key are concatenated separated by comma
2203                 // otherwise we wouldn't notice headers with same key added multiple times
2204                 StringBuffer combinedHeaderValue = new StringBuffer();
2205                 for ( int i = 0; headerValues.hasMoreElements(); i++ )
2206                 {
2207                     if ( i > 0 )
2208                     {
2209                         combinedHeaderValue.append( "," );
2210                     }
2211                     combinedHeaderValue.append( headerValues.nextElement() );
2212                 }
2213                 headers.put( name, combinedHeaderValue.toString() );
2214             }
2215 
2216             response.setContentType( "text/plain" );
2217             response.setStatus( HttpServletResponse.SC_OK );
2218             response.getWriter().print( "Hello, World!" );
2219 
2220             handlerRequestResponses.add(
2221                 new HandlerRequestResponse( baseRrequest.getMethod(), ( (Response) response ).getStatus(),
2222                                             baseRrequest.getRequestURI() ) );
2223 
2224             baseRrequest.setHandled( true );
2225         }
2226 
2227     }
2228 
2229     protected TestSecurityHandler createSecurityHandler()
2230     {
2231         Constraint constraint = new Constraint();
2232         constraint.setName( Constraint.__BASIC_AUTH );
2233         constraint.setRoles( new String[]{ "admin" } );
2234         constraint.setAuthenticate( true );
2235 
2236         ConstraintMapping cm = new ConstraintMapping();
2237         cm.setConstraint( constraint );
2238         cm.setPathSpec( "/*" );
2239 
2240         TestSecurityHandler sh = new TestSecurityHandler();
2241         HashLoginService hashLoginService = new HashLoginService( "MyRealm" );
2242         hashLoginService.putUser( "user", new Password( "secret" ), new String[] { "admin" } );
2243         sh.setLoginService( hashLoginService );
2244         sh.setConstraintMappings( new ConstraintMapping[]{ cm } );
2245         sh.setAuthenticator ( new BasicAuthenticator() );
2246         return sh;
2247     }
2248 
2249     /**
2250      *
2251      */
2252     @SuppressWarnings( "checkstyle:visibilitymodifier" )
2253     public static class TestSecurityHandler
2254         extends ConstraintSecurityHandler
2255     {
2256 
2257         public List<HandlerRequestResponse> handlerRequestResponses = new ArrayList<HandlerRequestResponse>();
2258 
2259         @Override
2260         public void handle( String target, Request baseRequest, HttpServletRequest request,
2261             HttpServletResponse response ) throws IOException, ServletException
2262         {
2263             String method = request.getMethod();
2264             super.handle( target, baseRequest, request, response );
2265 
2266             handlerRequestResponses.add(
2267                 new HandlerRequestResponse( method, ( (Response) response ).getStatus(), request.getRequestURI() ) );
2268         }
2269     }
2270 
2271     /**
2272      *
2273      */
2274     @SuppressWarnings( "checkstyle:visibilitymodifier" )
2275     public static class HandlerRequestResponse
2276     {
2277         public String method;
2278 
2279         public int responseCode;
2280 
2281         public String requestUri;
2282 
2283         private HandlerRequestResponse( String method, int responseCode, String requestUri )
2284         {
2285             this.method = method;
2286             this.responseCode = responseCode;
2287             this.requestUri = requestUri;
2288         }
2289 
2290         @Override
2291         public String toString()
2292         {
2293             final StringBuilder sb = new StringBuilder();
2294             sb.append( "HandlerRequestResponse" );
2295             sb.append( "{method='" ).append( method ).append( '\'' );
2296             sb.append( ", responseCode=" ).append( responseCode );
2297             sb.append( ", requestUri='" ).append( requestUri ).append( '\'' );
2298             sb.append( '}' );
2299             return sb.toString();
2300         }
2301     }
2302 
2303     /**
2304      * Verify a WagonException message contains required format and context based on the status code we expected to
2305      * trigger it in the first place.
2306      * <p>
2307      * This implementation represents the most desired assertions, but HttpWagonTestCase sub-classes could override
2308      * this method if a specific wagon representation makes it impossible to meet these assertions.
2309      *
2310      * @param e               an instance of {@link WagonException}
2311      * @param forStatusCode   the response status code that triggered the exception
2312      * @param forUrl          the url that triggered the exception
2313      * @param forReasonPhrase the optional status line reason phrase the server returned
2314      */
2315     protected void verifyWagonExceptionMessage( Exception e, int forStatusCode, String forUrl, String forReasonPhrase )
2316     {
2317         // TODO: handle AuthenticationException for Wagon.connect() calls
2318         assertNotNull( e );
2319         try
2320         {
2321             assertTrue( "only verify instances of WagonException", e instanceof WagonException );
2322 
2323             String reasonPhrase;
2324             String assertMessageForBadMessage = "exception message not described properly";
2325             switch ( forStatusCode )
2326             {
2327                 case HttpServletResponse.SC_NOT_FOUND:
2328                     // TODO: add test for 410: Gone?
2329                     assertTrue( "404 not found response should throw ResourceDoesNotExistException",
2330                             e instanceof ResourceDoesNotExistException );
2331                     reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Not Found" : ( " " + forReasonPhrase );
2332                     assertEquals( assertMessageForBadMessage, "Resource missing at " + forUrl + " 404"
2333                             + reasonPhrase, e.getMessage() );
2334                     break;
2335 
2336                 case HttpServletResponse.SC_UNAUTHORIZED:
2337                     // FIXME assumes Wagon.get()/put() returning 401 instead of Wagon.connect()
2338                     assertTrue( "401 Unauthorized should throw AuthorizationException since "
2339                                     + " AuthenticationException is not explicitly declared as thrown from wagon "
2340                                     + "methods",
2341                             e instanceof AuthorizationException );
2342                     reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Unauthorized" : ( " " + forReasonPhrase );
2343                     assertEquals( assertMessageForBadMessage, "Authentication failed for " + forUrl + " 401"
2344                             + reasonPhrase, e.getMessage() );
2345                     break;
2346 
2347                 case HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED:
2348                     assertTrue( "407 Proxy authentication required should throw AuthorizationException",
2349                             e instanceof AuthorizationException );
2350                     reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Proxy Authentication Required"
2351                             : ( " " + forReasonPhrase );
2352                     assertEquals( assertMessageForBadMessage, "HTTP proxy server authentication failed for "
2353                             + forUrl + " 407" + reasonPhrase, e.getMessage() );
2354                     break;
2355 
2356                 case HttpServletResponse.SC_FORBIDDEN:
2357                     assertTrue( "403 Forbidden should throw AuthorizationException",
2358                             e instanceof AuthorizationException );
2359                     reasonPhrase = StringUtils.isEmpty( forReasonPhrase ) ? " Forbidden" : ( " " + forReasonPhrase );
2360                     assertEquals( assertMessageForBadMessage, "Authorization failed for " + forUrl + " 403"
2361                             + reasonPhrase, e.getMessage() );
2362                     break;
2363 
2364                 default:
2365                     assertTrue( "transfer failures should at least be wrapped in a TransferFailedException", e
2366                                     instanceof TransferFailedException );
2367                     assertTrue( "expected status code for transfer failures should be >= 400",
2368                             forStatusCode >= HttpServletResponse.SC_BAD_REQUEST );
2369                     reasonPhrase = forReasonPhrase == null ? "" : " " + forReasonPhrase;
2370                     assertEquals( assertMessageForBadMessage, "Transfer failed for " + forUrl + " "
2371                             + forStatusCode + reasonPhrase, e.getMessage() );
2372                     break;
2373             }
2374         }
2375         catch ( AssertionError assertionError )
2376         {
2377             logger.error( "Exception which failed assertions: ", e );
2378             throw assertionError;
2379         }
2380 
2381     }
2382 
2383 }