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