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