001package org.apache.maven.wagon.http;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.wagon.FileTestUtils;
023import org.apache.maven.wagon.ResourceDoesNotExistException;
024import org.apache.maven.wagon.StreamingWagon;
025import org.apache.maven.wagon.StreamingWagonTestCase;
026import org.apache.maven.wagon.TransferFailedException;
027import org.apache.maven.wagon.Wagon;
028import org.apache.maven.wagon.authentication.AuthenticationInfo;
029import org.apache.maven.wagon.authorization.AuthorizationException;
030import org.apache.maven.wagon.proxy.ProxyInfo;
031import org.apache.maven.wagon.proxy.ProxyInfoProvider;
032import org.apache.maven.wagon.repository.Repository;
033import org.apache.maven.wagon.resource.Resource;
034import org.codehaus.plexus.util.FileUtils;
035import org.codehaus.plexus.util.IOUtil;
036import org.codehaus.plexus.util.StringUtils;
037import org.eclipse.jetty.security.ConstraintMapping;
038import org.eclipse.jetty.security.ConstraintSecurityHandler;
039import org.eclipse.jetty.security.HashLoginService;
040import org.eclipse.jetty.security.SecurityHandler;
041import org.eclipse.jetty.security.authentication.BasicAuthenticator;
042import org.eclipse.jetty.server.Request;
043import org.eclipse.jetty.server.Response;
044import org.eclipse.jetty.server.Server;
045import org.eclipse.jetty.server.handler.AbstractHandler;
046import org.eclipse.jetty.server.handler.HandlerCollection;
047import org.eclipse.jetty.util.security.Constraint;
048import org.eclipse.jetty.util.security.Password;
049import org.eclipse.jetty.servlet.DefaultServlet;
050import org.eclipse.jetty.servlet.ServletContextHandler;
051import org.eclipse.jetty.servlet.ServletHolder;
052
053import javax.servlet.ServletException;
054import javax.servlet.http.HttpServletRequest;
055import javax.servlet.http.HttpServletResponse;
056import java.io.ByteArrayOutputStream;
057import java.io.File;
058import java.io.FileInputStream;
059import java.io.FileOutputStream;
060import java.io.IOException;
061import java.io.InputStream;
062import java.io.OutputStream;
063import java.lang.reflect.Method;
064import java.net.URLDecoder;
065import java.util.ArrayList;
066import java.util.Collections;
067import java.util.Enumeration;
068import java.util.HashMap;
069import java.util.List;
070import java.util.Map;
071import java.util.Properties;
072import java.util.concurrent.atomic.AtomicBoolean;
073import java.util.zip.GZIPOutputStream;
074
075/**
076 *
077 */
078public abstract class HttpWagonTestCase
079    extends StreamingWagonTestCase
080{
081    public static final int SC_TOO_MANY_REQUESTS = 429;
082
083    private Server server;
084
085    protected void setupWagonTestingFixtures()
086        throws Exception
087    {
088        // File round trip testing
089
090        File file = FileTestUtils.createUniqueFile( "local-repository", "test-resource" );
091
092        file.delete();
093
094        file.getParentFile().mkdirs();
095
096        File repositoryDirectory = getRepositoryDirectory();
097        FileUtils.deleteDirectory( repositoryDirectory );
098        repositoryDirectory.mkdirs();
099
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}