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