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