001package org.apache.maven.wagon.providers.webdav;
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 it.could.webdav.DAVServlet;
023import org.apache.http.HttpException;
024import org.apache.http.client.methods.CloseableHttpResponse;
025import org.apache.http.client.methods.HttpUriRequest;
026import org.apache.maven.wagon.ResourceDoesNotExistException;
027import org.apache.maven.wagon.StreamingWagon;
028import org.apache.maven.wagon.TransferFailedException;
029import org.apache.maven.wagon.Wagon;
030import org.apache.maven.wagon.http.HttpWagonTestCase;
031import org.apache.maven.wagon.repository.Repository;
032import org.apache.maven.wagon.resource.Resource;
033import org.apache.maven.wagon.shared.http.HttpConfiguration;
034import org.apache.maven.wagon.shared.http.HttpMethodConfiguration;
035import org.eclipse.jetty.server.Server;
036import org.eclipse.jetty.servlet.ServletContextHandler;
037import org.eclipse.jetty.servlet.ServletHolder;
038
039import java.io.File;
040import java.io.IOException;
041import java.net.SocketTimeoutException;
042import java.util.List;
043import java.util.Properties;
044
045import javax.servlet.http.HttpServletResponse;
046
047/*
048 * WebDAV Wagon Test
049 *
050 * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
051 *
052 * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
053 */
054public class WebDavWagonTest
055    extends HttpWagonTestCase
056{
057
058    protected String getTestRepositoryUrl()
059        throws IOException
060    {
061        return getProtocol() + "://localhost:" + getTestRepositoryPort() + "/newfolder/folder2/";
062    }
063
064    protected String getProtocol()
065    {
066        return "dav";
067    }
068
069    protected ServletContextHandler createContext( Server server, File repositoryDirectory )
070        throws IOException
071    {
072        ServletContextHandler dav = new ServletContextHandler( ServletContextHandler.SESSIONS );
073        ServletHolder davServletHolder = new ServletHolder( new DAVServlet() );
074        davServletHolder.setInitParameter( "rootPath", repositoryDirectory.getAbsolutePath() );
075        davServletHolder.setInitParameter( "xmlOnly", "false" );
076        dav.addServlet( davServletHolder, "/*" );
077        return dav;
078    }
079
080    protected long getExpectedLastModifiedOnGet( Repository repository, Resource resource )
081    {
082        File file = new File( getDavRepository(), resource.getName() );
083        return ( file.lastModified() / 1000L ) * 1000L;
084    }
085
086
087    private File getDavRepository()
088    {
089        return getTestFile( "target/test-output/http-repository/newfolder/folder2" );
090    }
091
092    private void assertURL( String userUrl, String expectedUrl )
093    {
094        Repository repo = new Repository( "test-geturl", userUrl );
095        String actualUrl = ( new WebDavWagon() ).getURL( repo );
096        assertEquals( "WebDavWagon.getURL(" + userUrl + ")", expectedUrl, actualUrl );
097    }
098
099    /**
100     * Tests the maven 2.0.x way to define a webdav URL without SSL.
101     */
102    public void testGetURLDavHttp()
103    {
104        assertURL( "dav:http://localhost:9080/dav/",
105                   "http://localhost:9080/dav/" );
106    }
107
108    /**
109     * Tests the maven 2.0.x way to define a webdav URL with SSL.
110     */
111    public void testGetURLDavHttps()
112    {
113        assertURL( "dav:https://localhost:9443/dav/",
114                   "https://localhost:9443/dav/" );
115    }
116
117    /**
118     * Tests the URI spec way of defining a webdav URL without SSL.
119     */
120    public void testGetURLDavUri()
121    {
122        assertURL( "dav://localhost:9080/dav/",
123                   "http://localhost:9080/dav/" );
124    }
125
126    /**
127     * Tests the URI spec way of defining a webdav URL with SSL.
128     */
129    public void testGetURLDavUriWithSsl()
130    {
131        assertURL( "davs://localhost:9443/dav/",
132                   "https://localhost:9443/dav/" );
133    }
134
135    /**
136     * Tests the URI spec way of defining a webdav URL without SSL.
137     */
138    public void testGetURLDavPlusHttp()
139    {
140        assertURL( "dav+https://localhost:" + getTestRepositoryPort() + "/dav/",
141                   "https://localhost:" + getTestRepositoryPort() + "/dav/" );
142    }
143
144    /**
145     * Tests the URI spec way of defining a webdav URL with SSL.
146     */
147    public void testGetURLDavPlusHttps()
148    {
149        assertURL( "dav+https://localhost:9443/dav/",
150                   "https://localhost:9443/dav/" );
151    }
152
153    public void testMkdirs()
154        throws Exception
155    {
156        setupWagonTestingFixtures();
157
158        setupRepositories();
159
160        WebDavWagon wagon = (WebDavWagon) getWagon();
161        wagon.connect( testRepository, getAuthInfo() );
162
163        try
164        {
165            File dir = getRepositoryDirectory();
166
167            // check basedir also doesn't exist and will need to be created
168            dir = new File( dir, testRepository.getBasedir() );
169            assertFalse( dir.exists() );
170
171            // test leading /
172            assertFalse( new File( dir, "foo" ).exists() );
173            wagon.mkdirs( "/foo" );
174            assertTrue( new File( dir, "foo" ).exists() );
175
176            // test trailing /
177            assertFalse( new File( dir, "bar" ).exists() );
178            wagon.mkdirs( "bar/" );
179            assertTrue( new File( dir, "bar" ).exists() );
180
181            // test when already exists
182            wagon.mkdirs( "bar" );
183
184            // test several parts
185            assertFalse( new File( dir, "1/2/3/4" ).exists() );
186            wagon.mkdirs( "1/2/3/4" );
187            assertTrue( new File( dir, "1/2/3/4" ).exists() );
188
189            // test additional part and trailing /
190            assertFalse( new File( dir, "1/2/3/4/5" ).exists() );
191            wagon.mkdirs( "1/2/3/4/5/" );
192            assertTrue( new File( dir, "1/2/3/4" ).exists() );
193        }
194        finally
195        {
196            wagon.disconnect();
197
198            tearDownWagonTestingFixtures();
199        }
200    }
201
202    public void testMkdirsWithNoBasedir()
203        throws Exception
204    {
205        // WAGON-244
206        setupWagonTestingFixtures();
207
208        setupRepositories();
209
210        // reconstruct with no basedir
211        testRepository.setUrl(
212            testRepository.getProtocol() + "://" + testRepository.getHost() + ":" + testRepository.getPort() );
213
214        WebDavWagon wagon = (WebDavWagon) getWagon();
215        wagon.connect( testRepository, getAuthInfo() );
216
217        try
218        {
219            File dir = getRepositoryDirectory();
220
221            // check basedir also doesn't exist and will need to be created
222            dir = new File( dir, testRepository.getBasedir() );
223            assertTrue( dir.exists() );
224
225            // test leading /
226            assertFalse( new File( dir, "foo" ).exists() );
227            wagon.mkdirs( "/foo" );
228            assertTrue( new File( dir, "foo" ).exists() );
229        }
230        finally
231        {
232            wagon.disconnect();
233
234            tearDownWagonTestingFixtures();
235        }
236    }
237
238    protected void setHttpConfiguration( StreamingWagon wagon, Properties headers, Properties params )
239    {
240        HttpConfiguration config = new HttpConfiguration();
241
242        HttpMethodConfiguration methodConfiguration = new HttpMethodConfiguration();
243        methodConfiguration.setHeaders( headers );
244        methodConfiguration.setParams( params );
245        config.setAll( methodConfiguration );
246        ( (WebDavWagon) wagon ).setHttpConfiguration( config );
247    }
248
249    /**
250     * Make sure Wagon WebDAV can detect remote directory
251     *
252     * @throws Exception
253     */
254    public void testWagonWebDavGetFileList()
255        throws Exception
256    {
257        setupWagonTestingFixtures();
258
259        setupRepositories();
260
261        String dirName = "file-list";
262
263        String filenames[] =
264            new String[]{ "test-resource.txt", "test-resource.pom", "test-resource b.txt", "more-resources.dat" };
265
266        for ( int i = 0; i < filenames.length; i++ )
267        {
268            putFile( dirName + "/" + filenames[i], dirName + "/" + filenames[i], filenames[i] + "\n" );
269        }
270
271        String dirnames[] = new String[]{ "test-dir1", "test-dir2" };
272
273        for ( int i = 0; i < dirnames.length; i++ )
274        {
275            new File( getDavRepository(), dirName + "/" + dirnames[i] ).mkdirs();
276        }
277
278        Wagon wagon = getWagon();
279
280        wagon.connect( testRepository, getAuthInfo() );
281
282        List<String> list = wagon.getFileList( dirName );
283
284        assertNotNull( "file list should not be null.", list );
285        assertEquals( "file list should contain 6 items", 6, list.size() );
286
287        for ( int i = 0; i < filenames.length; i++ )
288        {
289            assertTrue( "Filename '" + filenames[i] + "' should be in list.", list.contains( filenames[i] ) );
290        }
291
292        for ( int i = 0; i < dirnames.length; i++ )
293        {
294            assertTrue( "Directory '" + dirnames[i] + "' should be in list.", list.contains( dirnames[i] + "/" ) );
295        }
296
297        ///////////////////////////////////////////////////////////////////////////
298        list = wagon.getFileList( "" );
299        assertNotNull( "file list should not be null.", list );
300        assertEquals( "file list should contain 1 items", 1, list.size() );
301
302        ///////////////////////////////////////////////////////////////////////////
303        list = wagon.getFileList( dirName + "/test-dir1" );
304        assertNotNull( "file list should not be null.", list );
305        assertEquals( "file list should contain 0 items", 0, list.size() );
306
307        /////////////////////////////////////////////////////////////////////////////
308        try
309        {
310            list = wagon.getFileList( dirName + "/test-dir-bogus" );
311            fail( "Exception expected" );
312        }
313        catch ( ResourceDoesNotExistException e )
314        {
315
316        }
317
318        wagon.disconnect();
319
320        tearDownWagonTestingFixtures();
321    }
322
323
324    public void testWagonFailsOnPutFailureByDefault()
325        throws Exception
326    {
327        setupWagonTestingFixtures();
328
329        setupRepositories();
330
331        File testFile = getTempFile();
332
333        System.clearProperty( WebDavWagon.CONTINUE_ON_FAILURE_PROPERTY );
334
335        WebDavWagon wagon = new TimeoutSimulatingWagon();
336        wagon.connect( testRepository, getAuthInfo() );
337
338        try
339        {
340            String filename = TimeoutSimulatingWagon.TIMEOUT_TRIGGER + ".txt";
341
342            try
343            {
344                wagon.put( testFile, filename );
345                fail( "Exception expected" );
346            }
347            catch ( TransferFailedException e )
348            {
349
350            }
351        }
352        finally
353        {
354            wagon.disconnect();
355
356            tearDownWagonTestingFixtures();
357        }
358    }
359
360    private File getTempFile()
361        throws IOException
362    {
363        File inputFile = File.createTempFile( "test-resource", ".txt" );
364        inputFile.deleteOnExit();
365        return inputFile;
366    }
367
368    private static class TimeoutSimulatingWagon
369        extends WebDavWagon
370    {
371        private static final String TIMEOUT_TRIGGER = "timeout";
372
373        protected CloseableHttpResponse execute( HttpUriRequest httpRequestBase )
374            throws HttpException, IOException
375        {
376            if ( httpRequestBase.getURI().getPath().contains( TIMEOUT_TRIGGER ) )
377            {
378                throw new SocketTimeoutException( "Timeout triggered by request for '" + httpRequestBase.getURI().getPath() + "'" );
379            }
380            else
381            {
382                return super.execute( httpRequestBase );
383            }
384        }
385    }
386
387    public void testWagonContinuesOnPutFailureIfPropertySet()
388        throws Exception
389    {
390        setupWagonTestingFixtures();
391
392        setupRepositories();
393
394        File testFile = getTempFile();
395
396        String continueOnFailureProperty = WebDavWagon.CONTINUE_ON_FAILURE_PROPERTY;
397        System.setProperty( continueOnFailureProperty, "true" );
398
399        WebDavWagon wagon = new TimeoutSimulatingWagon();
400        wagon.connect( testRepository, getAuthInfo() );
401
402        try
403        {
404            String filename = TimeoutSimulatingWagon.TIMEOUT_TRIGGER + ".txt";
405
406            wagon.put( testFile, filename );
407        }
408        finally
409        {
410            wagon.disconnect();
411
412            System.clearProperty( continueOnFailureProperty );
413
414            tearDownWagonTestingFixtures();
415        }
416    }
417
418    @Override
419    protected boolean supportPreemptiveAuthenticationPut()
420    {
421        return false;
422    }
423
424    @Override
425    protected boolean supportPreemptiveAuthenticationGet()
426    {
427        return false;
428    }
429
430    @Override
431    protected boolean supportProxyPreemptiveAuthentication()
432    {
433        return true;
434    }
435
436    protected void testPreemptiveAuthenticationGet( TestSecurityHandler sh, boolean preemptive )
437    {
438        if ( preemptive )
439        {
440            assertEquals( "testPreemptiveAuthenticationGet preemptive=true: expected 1 request, got "
441                + sh.handlerRequestResponses, 1, sh.handlerRequestResponses.size() );
442            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 0 ).responseCode );
443        }
444        else
445        {
446            assertEquals( "testPreemptiveAuthenticationGet preemptive=false: expected 2 requests (401,200), got "
447                + sh.handlerRequestResponses, 2, sh.handlerRequestResponses.size() );
448            assertEquals( HttpServletResponse.SC_UNAUTHORIZED, sh.handlerRequestResponses.get( 0 ).responseCode );
449            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 1 ).responseCode );
450        }
451    }
452
453    protected void testPreemptiveAuthenticationPut( TestSecurityHandler sh, boolean preemptive )
454    {
455        if ( preemptive )
456        {
457            assertEquals( "testPreemptiveAuthenticationPut preemptive=true: expected 2 requests (200,201), got "
458                + sh.handlerRequestResponses, 2, sh.handlerRequestResponses.size() );
459            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 0 ).responseCode );
460            assertEquals( HttpServletResponse.SC_CREATED, sh.handlerRequestResponses.get( 1 ).responseCode );
461        }
462        else
463        {
464            assertEquals( "testPreemptiveAuthenticationPut preemptive=false: expected 3 requests (401,200,201), got "
465                + sh.handlerRequestResponses, 3, sh.handlerRequestResponses.size() );
466            assertEquals( HttpServletResponse.SC_UNAUTHORIZED, sh.handlerRequestResponses.get( 0 ).responseCode );
467            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 1 ).responseCode );
468            assertEquals( HttpServletResponse.SC_CREATED, sh.handlerRequestResponses.get( 2 ).responseCode );
469        }
470    }
471
472
473    /* This method cannot be reasonable used to represend GET and PUT for WebDAV, it would contain too much
474     * duplicate code. Leave as-is, but don't use it.
475     */
476    protected void testPreemptiveAuthentication( TestSecurityHandler sh, boolean preemptive )
477    {
478        if ( preemptive )
479        {
480            assertEquals( "testPreemptiveAuthentication preemptive=false: expected 2 requests (200,.), got "
481                + sh.handlerRequestResponses, 2, sh.handlerRequestResponses.size() );
482            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 0 ).responseCode );
483        }
484        else
485        {
486            assertEquals( "testPreemptiveAuthentication preemptive=false: expected 3 requests (401,200,200), got "
487                + sh.handlerRequestResponses, 3, sh.handlerRequestResponses.size() );
488            assertEquals( HttpServletResponse.SC_UNAUTHORIZED, sh.handlerRequestResponses.get( 0 ).responseCode );
489            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 1 ).responseCode );
490            assertEquals( HttpServletResponse.SC_OK, sh.handlerRequestResponses.get( 2 ).responseCode );
491
492        }
493    }
494
495    @Override
496    protected void checkRequestResponseForRedirectPutWithFullUrl( RedirectHandler redirectHandler,
497                                                                  PutHandler putHandler )
498    {
499        assertEquals( "found:" + putHandler.handlerRequestResponses, 1, putHandler.handlerRequestResponses.size() );
500        assertEquals( "found:" + putHandler.handlerRequestResponses, HttpServletResponse.SC_CREATED,
501                      putHandler.handlerRequestResponses.get( 0 ).responseCode );
502        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 2,
503                      redirectHandler.handlerRequestResponses.size() );
504        assertEquals( "found:" + redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER,
505                      redirectHandler.handlerRequestResponses.get( 0 ).responseCode );
506    }
507
508    @Override
509    protected void checkRequestResponseForRedirectPutWithRelativeUrl( RedirectHandler redirectHandler,
510                                                                      PutHandler putHandler )
511    {
512        assertEquals( "found:" + putHandler.handlerRequestResponses, 0, putHandler.handlerRequestResponses.size() );
513
514        assertEquals( "found:" + redirectHandler.handlerRequestResponses, 4,
515                      redirectHandler.handlerRequestResponses.size() );
516        assertEquals( "found:" + redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER,
517                      redirectHandler.handlerRequestResponses.get( 0 ).responseCode );
518        assertEquals( "found:" + redirectHandler.handlerRequestResponses, HttpServletResponse.SC_OK,
519                      redirectHandler.handlerRequestResponses.get( 1 ).responseCode );
520        assertEquals( "found:" + redirectHandler.handlerRequestResponses, HttpServletResponse.SC_SEE_OTHER,
521                      redirectHandler.handlerRequestResponses.get( 2 ).responseCode );
522
523    }
524
525    @Override
526    protected void verifyWagonExceptionMessage( Exception e, int forStatusCode, String forUrl, String forReasonPhrase )
527    {
528        Repository repo = new Repository( "test-geturl", forUrl );
529        String expectedMessageUrl = ( new WebDavWagon() ).getURL( repo );
530        super.verifyWagonExceptionMessage( e, forStatusCode, expectedMessageUrl, forReasonPhrase );
531    }
532}