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