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