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