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