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