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