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