Coverage Report - org.apache.maven.wagon.providers.http.LightweightHttpWagon
 
Classes in this File Line Coverage Branch Coverage Complexity
LightweightHttpWagon
85 %
126/147
69 %
57/82
4,333
 
 1  
 package org.apache.maven.wagon.providers.http;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.apache.maven.wagon.ConnectionException;
 23  
 import org.apache.maven.wagon.InputData;
 24  
 import org.apache.maven.wagon.OutputData;
 25  
 import org.apache.maven.wagon.ResourceDoesNotExistException;
 26  
 import org.apache.maven.wagon.StreamWagon;
 27  
 import org.apache.maven.wagon.TransferFailedException;
 28  
 import org.apache.maven.wagon.authentication.AuthenticationException;
 29  
 import org.apache.maven.wagon.authorization.AuthorizationException;
 30  
 import org.apache.maven.wagon.events.TransferEvent;
 31  
 import org.apache.maven.wagon.proxy.ProxyInfo;
 32  
 import org.apache.maven.wagon.resource.Resource;
 33  
 import org.apache.maven.wagon.shared.http4.HtmlFileListParser;
 34  
 import org.codehaus.plexus.util.Base64;
 35  
 
 36  
 import java.io.FileNotFoundException;
 37  
 import java.io.IOException;
 38  
 import java.io.InputStream;
 39  
 import java.io.OutputStream;
 40  
 import java.net.HttpURLConnection;
 41  
 import java.net.InetSocketAddress;
 42  
 import java.net.MalformedURLException;
 43  
 import java.net.PasswordAuthentication;
 44  
 import java.net.Proxy;
 45  
 import java.net.Proxy.Type;
 46  
 import java.net.SocketAddress;
 47  
 import java.net.URL;
 48  
 import java.util.ArrayList;
 49  
 import java.util.Iterator;
 50  
 import java.util.List;
 51  
 import java.util.Properties;
 52  
 import java.util.zip.GZIPInputStream;
 53  
 
 54  
 /**
 55  
  * LightweightHttpWagon, using JDK's HttpURLConnection.
 56  
  *
 57  
  * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
 58  
  * @version $Id: LightweightHttpWagon.java 1208429 2011-11-30 13:54:26Z olamy $
 59  
  * @plexus.component role="org.apache.maven.wagon.Wagon" role-hint="http" instantiation-strategy="per-lookup"
 60  
  * @see HttpURLConnection
 61  
  */
 62  215
 public class LightweightHttpWagon
 63  
     extends StreamWagon
 64  
 {
 65  
     private boolean preemptiveAuthentication;
 66  
 
 67  
     private HttpURLConnection putConnection;
 68  
 
 69  215
     private Proxy proxy = Proxy.NO_PROXY;
 70  
 
 71  
     public static final int MAX_REDIRECTS = 10;
 72  
 
 73  
     /**
 74  
      * Whether to use any proxy cache or not.
 75  
      *
 76  
      * @plexus.configuration default="false"
 77  
      */
 78  
     private boolean useCache;
 79  
 
 80  
     /**
 81  
      * @plexus.configuration
 82  
      */
 83  
     private Properties httpHeaders;
 84  
 
 85  
     /**
 86  
      * @plexus.requirement
 87  
      */
 88  
     private LightweightHttpWagonAuthenticator authenticator;
 89  
 
 90  
     /**
 91  
      * Builds a complete URL string from the repository URL and the relative path passed.
 92  
      *
 93  
      * @param path the relative path
 94  
      * @return the complete URL
 95  
      */
 96  
     private String buildUrl( String path )
 97  
     {
 98  245
         final String repoUrl = getRepository().getUrl();
 99  
 
 100  245
         path = path.replace( ' ', '+' );
 101  
 
 102  245
         if ( repoUrl.charAt( repoUrl.length() - 1 ) != '/' )
 103  
         {
 104  137
             return repoUrl + '/' + path;
 105  
         }
 106  
 
 107  108
         return repoUrl + path;
 108  
     }
 109  
 
 110  
     public void fillInputData( InputData inputData )
 111  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 112  
     {
 113  112
         Resource resource = inputData.getResource();
 114  
 
 115  112
         String visitingUrl = buildUrl( resource.getName() );
 116  
         try
 117  
         {
 118  112
             List<String> visitedUrls = new ArrayList<String>();
 119  
 
 120  112
             for ( int redirectCount = 0; redirectCount < MAX_REDIRECTS; redirectCount++ )
 121  
             {
 122  112
                 if ( visitedUrls.contains( visitingUrl ) )
 123  
                 {
 124  0
                     throw new TransferFailedException( "Cyclic http redirect detected. Aborting! " + visitingUrl );
 125  
                 }
 126  112
                 visitedUrls.add( visitingUrl );
 127  
 
 128  112
                 URL url = new URL( visitingUrl );
 129  112
                 HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection( this.proxy );
 130  
 
 131  112
                 urlConnection.setRequestProperty( "Accept-Encoding", "gzip" );
 132  112
                 if ( !useCache )
 133  
                 {
 134  112
                     urlConnection.setRequestProperty( "Pragma", "no-cache" );
 135  
                 }
 136  
 
 137  112
                 addHeaders( urlConnection );
 138  
 
 139  
                 // TODO: handle all response codes
 140  112
                 int responseCode = urlConnection.getResponseCode();
 141  102
                 if ( responseCode == HttpURLConnection.HTTP_FORBIDDEN
 142  
                     || responseCode == HttpURLConnection.HTTP_UNAUTHORIZED )
 143  
                 {
 144  13
                     throw new AuthorizationException( "Access denied to: " + buildUrl( resource.getName() ) );
 145  
                 }
 146  89
                 if ( responseCode == HttpURLConnection.HTTP_MOVED_PERM
 147  
                     || responseCode == HttpURLConnection.HTTP_MOVED_TEMP )
 148  
                 {
 149  0
                     visitingUrl = urlConnection.getHeaderField( "Location" );
 150  0
                     continue;
 151  
                 }
 152  
 
 153  89
                 InputStream is = urlConnection.getInputStream();
 154  62
                 String contentEncoding = urlConnection.getHeaderField( "Content-Encoding" );
 155  62
                 boolean isGZipped = contentEncoding != null && "gzip".equalsIgnoreCase( contentEncoding );
 156  62
                 if ( isGZipped )
 157  
                 {
 158  3
                     is = new GZIPInputStream( is );
 159  
                 }
 160  62
                 inputData.setInputStream( is );
 161  62
                 resource.setLastModified( urlConnection.getLastModified() );
 162  62
                 resource.setContentLength( urlConnection.getContentLength() );
 163  62
                 break;
 164  
             }
 165  
         }
 166  0
         catch ( MalformedURLException e )
 167  
         {
 168  0
             throw new ResourceDoesNotExistException( "Invalid repository URL: " + e.getMessage(), e );
 169  
         }
 170  20
         catch ( FileNotFoundException e )
 171  
         {
 172  20
             throw new ResourceDoesNotExistException( "Unable to locate resource in repository", e );
 173  
         }
 174  17
         catch ( IOException e )
 175  
         {
 176  17
             StringBuilder message = new StringBuilder( "Error transferring file: " );
 177  17
             message.append( e.getMessage() );
 178  17
             message.append( " from " + visitingUrl );
 179  17
             if ( getProxyInfo() != null && getProxyInfo().getHost() != null )
 180  
             {
 181  0
                 message.append( " with proxyInfo " ).append( getProxyInfo().toString() );
 182  
             }
 183  17
             throw new TransferFailedException( message.toString(), e );
 184  62
         }
 185  62
     }
 186  
 
 187  
     private void addHeaders( HttpURLConnection urlConnection )
 188  
     {
 189  205
         if ( httpHeaders != null )
 190  
         {
 191  6
             for ( Iterator<?> i = httpHeaders.keySet().iterator(); i.hasNext(); )
 192  
             {
 193  6
                 String header = (String) i.next();
 194  6
                 urlConnection.setRequestProperty( header, httpHeaders.getProperty( header ) );
 195  6
             }
 196  
         }
 197  205
         setAuthorization( urlConnection );
 198  205
     }
 199  
 
 200  
     private void setAuthorization( HttpURLConnection urlConnection )
 201  
     {
 202  205
         if ( preemptiveAuthentication && authenticationInfo != null && authenticationInfo.getUserName() != null )
 203  
         {
 204  8
             String credentials = authenticationInfo.getUserName() + ":" + authenticationInfo.getPassword();
 205  8
             String encoded = new String( Base64.encodeBase64( credentials.getBytes() ) );
 206  8
             urlConnection.setRequestProperty( "Authorization", "Basic " + encoded );
 207  
         }
 208  205
     }
 209  
 
 210  
     public void fillOutputData( OutputData outputData )
 211  
         throws TransferFailedException
 212  
     {
 213  66
         Resource resource = outputData.getResource();
 214  
         try
 215  
         {
 216  66
             URL url = new URL( buildUrl( resource.getName() ) );
 217  66
             putConnection = (HttpURLConnection) url.openConnection( this.proxy );
 218  
 
 219  66
             addHeaders( putConnection );
 220  
 
 221  66
             putConnection.setRequestMethod( "PUT" );
 222  66
             putConnection.setDoOutput( true );
 223  66
             outputData.setOutputStream( putConnection.getOutputStream() );
 224  
         }
 225  0
         catch ( IOException e )
 226  
         {
 227  0
             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
 228  66
         }
 229  66
     }
 230  
 
 231  
     protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
 232  
         throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
 233  
     {
 234  
         try
 235  
         {
 236  66
             int statusCode = putConnection.getResponseCode();
 237  
 
 238  66
             switch ( statusCode )
 239  
             {
 240  
                 // Success Codes
 241  
                 case HttpURLConnection.HTTP_OK: // 200
 242  
                 case HttpURLConnection.HTTP_CREATED: // 201
 243  
                 case HttpURLConnection.HTTP_ACCEPTED: // 202
 244  
                 case HttpURLConnection.HTTP_NO_CONTENT: // 204
 245  51
                     break;
 246  
 
 247  
                 case HttpURLConnection.HTTP_FORBIDDEN:
 248  3
                     throw new AuthorizationException( "Access denied to: " + buildUrl( resource.getName() ) );
 249  
 
 250  
                 case HttpURLConnection.HTTP_NOT_FOUND:
 251  3
                     throw new ResourceDoesNotExistException(
 252  
                         "File: " + buildUrl( resource.getName() ) + " does not exist" );
 253  
 
 254  
                     // add more entries here
 255  
                 default:
 256  9
                     throw new TransferFailedException(
 257  
                         "Failed to transfer file: " + buildUrl( resource.getName() ) + ". Return code is: "
 258  
                             + statusCode );
 259  
             }
 260  
         }
 261  0
         catch ( IOException e )
 262  
         {
 263  0
             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
 264  
 
 265  0
             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
 266  51
         }
 267  51
     }
 268  
 
 269  
     protected void openConnectionInternal()
 270  
         throws ConnectionException, AuthenticationException
 271  
     {
 272  199
         final ProxyInfo proxyInfo = getProxyInfo( "http", getRepository().getHost() );
 273  199
         if ( proxyInfo != null )
 274  
         {
 275  6
             this.proxy = getProxy( proxyInfo );
 276  
         }
 277  199
         authenticator.setWagon( this );
 278  
 
 279  199
         boolean usePreemptiveAuthentication =
 280  
             Boolean.getBoolean( "maven.wagon.http.preemptiveAuthentication" ) || Boolean.parseBoolean(
 281  
                 repository.getParameter( "preemptiveAuthentication" ) ) || this.preemptiveAuthentication;
 282  
 
 283  199
         setPreemptiveAuthentication( usePreemptiveAuthentication );
 284  199
     }
 285  
 
 286  
     @SuppressWarnings( "deprecation" )
 287  
     public PasswordAuthentication requestProxyAuthentication()
 288  
     {
 289  3
         if ( proxyInfo != null && proxyInfo.getUserName() != null )
 290  
         {
 291  3
             String password = "";
 292  3
             if ( proxyInfo.getPassword() != null )
 293  
             {
 294  3
                 password = proxyInfo.getPassword();
 295  
             }
 296  3
             return new PasswordAuthentication( proxyInfo.getUserName(), password.toCharArray() );
 297  
         }
 298  0
         return null;
 299  
     }
 300  
 
 301  
     public PasswordAuthentication requestServerAuthentication()
 302  
     {
 303  239
         if ( authenticationInfo != null && authenticationInfo.getUserName() != null )
 304  
         {
 305  230
             String password = "";
 306  230
             if ( authenticationInfo.getPassword() != null )
 307  
             {
 308  230
                 password = authenticationInfo.getPassword();
 309  
             }
 310  230
             return new PasswordAuthentication( authenticationInfo.getUserName(), password.toCharArray() );
 311  
         }
 312  9
         return null;
 313  
     }
 314  
 
 315  
     private Proxy getProxy( ProxyInfo proxyInfo )
 316  
     {
 317  6
         return new Proxy( getProxyType( proxyInfo ), getSocketAddress( proxyInfo ) );
 318  
     }
 319  
 
 320  
     private Type getProxyType( ProxyInfo proxyInfo )
 321  
     {
 322  6
         if ( ProxyInfo.PROXY_SOCKS4.equals( proxyInfo.getType() ) || ProxyInfo.PROXY_SOCKS5.equals(
 323  
             proxyInfo.getType() ) )
 324  
         {
 325  0
             return Type.SOCKS;
 326  
         }
 327  
         else
 328  
         {
 329  6
             return Type.HTTP;
 330  
         }
 331  
     }
 332  
 
 333  
     public SocketAddress getSocketAddress( ProxyInfo proxyInfo )
 334  
     {
 335  6
         return InetSocketAddress.createUnresolved( proxyInfo.getHost(), proxyInfo.getPort() );
 336  
     }
 337  
 
 338  
     public void closeConnection()
 339  
         throws ConnectionException
 340  
     {
 341  203
         if ( putConnection != null )
 342  
         {
 343  66
             putConnection.disconnect();
 344  
         }
 345  203
         authenticator.resetWagon();
 346  203
     }
 347  
 
 348  
     public List<String> getFileList( String destinationDirectory )
 349  
         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
 350  
     {
 351  9
         InputData inputData = new InputData();
 352  
 
 353  9
         if ( destinationDirectory.length() > 0 && !destinationDirectory.endsWith( "/" ) )
 354  
         {
 355  6
             destinationDirectory += "/";
 356  
         }
 357  
 
 358  9
         String url = buildUrl( destinationDirectory );
 359  
 
 360  9
         Resource resource = new Resource( destinationDirectory );
 361  
 
 362  9
         inputData.setResource( resource );
 363  
 
 364  9
         fillInputData( inputData );
 365  
 
 366  6
         InputStream is = inputData.getInputStream();
 367  
 
 368  6
         if ( is == null )
 369  
         {
 370  0
             throw new TransferFailedException(
 371  
                 url + " - Could not open input stream for resource: '" + resource + "'" );
 372  
         }
 373  
 
 374  6
         return HtmlFileListParser.parseFileList( url, is );
 375  
     }
 376  
 
 377  
     public boolean resourceExists( String resourceName )
 378  
         throws TransferFailedException, AuthorizationException
 379  
     {
 380  
         HttpURLConnection headConnection;
 381  
 
 382  
         try
 383  
         {
 384  27
             URL url = new URL( buildUrl( new Resource( resourceName ).getName() ) );
 385  27
             headConnection = (HttpURLConnection) url.openConnection( this.proxy );
 386  
 
 387  27
             addHeaders( headConnection );
 388  
 
 389  27
             headConnection.setRequestMethod( "HEAD" );
 390  27
             headConnection.setDoOutput( true );
 391  
 
 392  27
             int statusCode = headConnection.getResponseCode();
 393  
 
 394  27
             switch ( statusCode )
 395  
             {
 396  
                 case HttpURLConnection.HTTP_OK:
 397  6
                     return true;
 398  
 
 399  
                 case HttpURLConnection.HTTP_FORBIDDEN:
 400  3
                     throw new AuthorizationException( "Access denied to: " + url );
 401  
 
 402  
                 case HttpURLConnection.HTTP_NOT_FOUND:
 403  9
                     return false;
 404  
 
 405  
                 case HttpURLConnection.HTTP_UNAUTHORIZED:
 406  6
                     throw new AuthorizationException( "Access denied to: " + url );
 407  
 
 408  
                 default:
 409  3
                     throw new TransferFailedException(
 410  
                         "Failed to look for file: " + buildUrl( resourceName ) + ". Return code is: " + statusCode );
 411  
             }
 412  
         }
 413  0
         catch ( IOException e )
 414  
         {
 415  0
             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
 416  
         }
 417  
     }
 418  
 
 419  
     public boolean isUseCache()
 420  
     {
 421  0
         return useCache;
 422  
     }
 423  
 
 424  
     public void setUseCache( boolean useCache )
 425  
     {
 426  0
         this.useCache = useCache;
 427  0
     }
 428  
 
 429  
     public Properties getHttpHeaders()
 430  
     {
 431  0
         return httpHeaders;
 432  
     }
 433  
 
 434  
     public void setHttpHeaders( Properties httpHeaders )
 435  
     {
 436  6
         this.httpHeaders = httpHeaders;
 437  6
     }
 438  
 
 439  
     void setSystemProperty( String key, String value )
 440  
     {
 441  275
         if ( value != null )
 442  
         {
 443  0
             System.setProperty( key, value );
 444  
         }
 445  
         else
 446  
         {
 447  275
             System.getProperties().remove( key );
 448  
         }
 449  275
     }
 450  
 
 451  
     public void setPreemptiveAuthentication( boolean preemptiveAuthentication )
 452  
     {
 453  258
         this.preemptiveAuthentication = preemptiveAuthentication;
 454  258
     }
 455  
 }