001package org.apache.maven.wagon.shared.http4; 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 java.io.ByteArrayInputStream; 023import java.io.File; 024import java.io.FileInputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.OutputStream; 028import java.net.URLEncoder; 029import java.nio.ByteBuffer; 030import java.security.cert.X509Certificate; 031import java.text.SimpleDateFormat; 032import java.util.Date; 033import java.util.Locale; 034import java.util.Map; 035import java.util.Properties; 036import java.util.TimeZone; 037import java.util.zip.GZIPInputStream; 038 039import javax.net.ssl.HttpsURLConnection; 040import javax.net.ssl.SSLException; 041import javax.net.ssl.SSLSession; 042import javax.net.ssl.SSLSocket; 043 044import org.apache.http.Header; 045import org.apache.http.HttpEntity; 046import org.apache.http.HttpException; 047import org.apache.http.HttpHost; 048import org.apache.http.HttpResponse; 049import org.apache.http.HttpStatus; 050import org.apache.http.auth.AuthScope; 051import org.apache.http.auth.Credentials; 052import org.apache.http.auth.NTCredentials; 053import org.apache.http.auth.UsernamePasswordCredentials; 054import org.apache.http.client.AuthCache; 055import org.apache.http.client.methods.HttpGet; 056import org.apache.http.client.methods.HttpHead; 057import org.apache.http.client.methods.HttpPut; 058import org.apache.http.client.methods.HttpUriRequest; 059import org.apache.http.client.params.ClientPNames; 060import org.apache.http.client.params.CookiePolicy; 061import org.apache.http.client.protocol.ClientContext; 062import org.apache.http.conn.ClientConnectionManager; 063import org.apache.http.conn.params.ConnRoutePNames; 064import org.apache.http.conn.scheme.PlainSocketFactory; 065import org.apache.http.conn.scheme.Scheme; 066import org.apache.http.conn.scheme.SchemeRegistry; 067import org.apache.http.conn.ssl.SSLSocketFactory; 068import org.apache.http.conn.ssl.X509HostnameVerifier; 069import org.apache.http.entity.AbstractHttpEntity; 070import org.apache.http.impl.auth.BasicScheme; 071import org.apache.http.impl.client.BasicAuthCache; 072import org.apache.http.impl.client.DefaultHttpClient; 073import org.apache.http.impl.conn.BasicClientConnectionManager; 074import org.apache.http.impl.conn.PoolingClientConnectionManager; 075import org.apache.http.impl.cookie.DateParseException; 076import org.apache.http.impl.cookie.DateUtils; 077import org.apache.http.message.BasicHeader; 078import org.apache.http.params.CoreConnectionPNames; 079import org.apache.http.params.CoreProtocolPNames; 080import org.apache.http.params.HttpParams; 081import org.apache.http.protocol.BasicHttpContext; 082import org.apache.maven.wagon.InputData; 083import org.apache.maven.wagon.OutputData; 084import org.apache.maven.wagon.PathUtils; 085import org.apache.maven.wagon.ResourceDoesNotExistException; 086import org.apache.maven.wagon.StreamWagon; 087import org.apache.maven.wagon.TransferFailedException; 088import org.apache.maven.wagon.Wagon; 089import org.apache.maven.wagon.authorization.AuthorizationException; 090import org.apache.maven.wagon.events.TransferEvent; 091import org.apache.maven.wagon.proxy.ProxyInfo; 092import org.apache.maven.wagon.repository.Repository; 093import org.apache.maven.wagon.resource.Resource; 094import org.codehaus.plexus.util.IOUtil; 095import org.codehaus.plexus.util.StringUtils; 096 097/** 098 * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a> 099 * @author <a href="mailto:james@atlassian.com">James William Dumay</a> 100 */ 101public abstract class AbstractHttpClientWagon 102 extends StreamWagon 103{ 104 105 private BasicHttpContext localContext; 106 107 private final class RequestEntityImplementation 108 extends AbstractHttpEntity 109 { 110 111 private final static int BUFFER_SIZE = 2048; 112 113 private final Resource resource; 114 115 private final Wagon wagon; 116 117 private ByteBuffer byteBuffer; 118 119 private File source; 120 121 private long length = -1; 122 123 private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon, 124 final File source ) 125 throws TransferFailedException 126 { 127 if ( source != null ) 128 { 129 this.source = source; 130 } 131 else 132 { 133 try 134 { 135 byte[] bytes = IOUtil.toByteArray( stream ); 136 byteBuffer = ByteBuffer.allocate( bytes.length ); 137 byteBuffer.put( bytes ); 138 } 139 catch ( IOException e ) 140 { 141 throw new TransferFailedException( e.getMessage(), e ); 142 } 143 } 144 this.resource = resource; 145 this.length = resource == null ? -1 : resource.getContentLength(); 146 147 this.wagon = wagon; 148 } 149 150 public long getContentLength() 151 { 152 return length; 153 } 154 155 public InputStream getContent() 156 throws IOException, IllegalStateException 157 { 158 if ( this.source != null ) 159 { 160 return new FileInputStream( this.source ); 161 } 162 return new ByteArrayInputStream( this.byteBuffer.array() ); 163 } 164 165 public boolean isRepeatable() 166 { 167 return true; 168 } 169 170 public void writeTo( final OutputStream outstream ) 171 throws IOException 172 { 173 if ( outstream == null ) 174 { 175 throw new IllegalArgumentException( "Output stream may not be null" ); 176 } 177 TransferEvent transferEvent = 178 new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT ); 179 transferEvent.setTimestamp( System.currentTimeMillis() ); 180 InputStream instream = ( this.source != null ) 181 ? new FileInputStream( this.source ) 182 : new ByteArrayInputStream( this.byteBuffer.array() ); 183 try 184 { 185 byte[] buffer = new byte[BUFFER_SIZE]; 186 int l; 187 if ( this.length < 0 ) 188 { 189 // until EOF 190 while ( ( l = instream.read( buffer ) ) != -1 ) 191 { 192 fireTransferProgress( transferEvent, buffer, -1 ); 193 outstream.write( buffer, 0, l ); 194 } 195 } 196 else 197 { 198 // no need to consume more than length 199 long remaining = this.length; 200 while ( remaining > 0 ) 201 { 202 l = instream.read( buffer, 0, (int) Math.min( BUFFER_SIZE, remaining ) ); 203 if ( l == -1 ) 204 { 205 break; 206 } 207 fireTransferProgress( transferEvent, buffer, (int) Math.min( BUFFER_SIZE, remaining ) ); 208 outstream.write( buffer, 0, l ); 209 remaining -= l; 210 } 211 } 212 } 213 finally 214 { 215 instream.close(); 216 } 217 } 218 219 public boolean isStreaming() 220 { 221 return true; 222 } 223 } 224 225 protected static final int SC_NULL = -1; 226 227 protected static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" ); 228 229 private DefaultHttpClient client; 230 231 /** 232 * @since 2.0 233 */ 234 protected static ClientConnectionManager connectionManagerPooled; 235 236 /** 237 * @since 2.0 238 */ 239 protected ClientConnectionManager clientConnectionManager = 240 new BasicClientConnectionManager( createSchemeRegistry() ); 241 242 /** 243 * use http(s) connection pool mechanism. 244 * <b>enabled by default</b> 245 * 246 * @since 2.0 247 */ 248 protected static boolean useClientManagerPooled = 249 Boolean.valueOf( System.getProperty( "maven.wagon.http.pool", "true" ) ); 250 251 /** 252 * skip failure on certificate validity checks. 253 * <b>disabled by default</b> 254 * 255 * @since 2.0 256 */ 257 protected static boolean sslInsecure = Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.insecure", "false" ) ); 258 259 /** 260 * if using sslInsecure, certificate date issues will be ignored 261 * <b>disabled by default</b> 262 * 263 * @since 2.0 264 */ 265 protected static boolean IGNORE_SSL_VALIDITY_DATES = 266 Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.ignore.validity.dates", "false" ) ); 267 268 /** 269 * If enabled, ssl hostname verifier does not check hostname. Disable this will use a browser compat hostname verifier 270 * <b>disabled by default</b> 271 * 272 * @since 2.0 273 * @see BrowserCompatHostnameVerifier 274 */ 275 protected static boolean sslAllowAll = 276 Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.allowall", "false" ) ); 277 278 private static SchemeRegistry createSchemeRegistry() 279 { 280 SchemeRegistry schemeRegistry = new SchemeRegistry(); 281 schemeRegistry.register( new Scheme( "http", 80, PlainSocketFactory.getSocketFactory() ) ); 282 SSLSocketFactory sslSocketFactory; 283 if ( sslInsecure ) 284 { 285 try 286 { 287 sslSocketFactory = new SSLSocketFactory( 288 RelaxedX509TrustManager.createRelaxedSSLContext(), 289 sslAllowAll ? new RelaxedHostNameVerifier() : SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER ); 290 } 291 catch ( IOException e ) 292 { 293 throw new RuntimeException( "failed to init SSLSocket Factory " + e.getMessage(), e ); 294 } 295 } 296 else 297 { 298 sslSocketFactory = new SSLSocketFactory( 299 HttpsURLConnection.getDefaultSSLSocketFactory(), 300 SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER ); 301 } 302 303 Scheme httpsScheme = new Scheme( "https", 443, new ConfigurableSSLSocketFactoryDecorator( sslSocketFactory ) ); 304 schemeRegistry.register( httpsScheme ); 305 306 return schemeRegistry; 307 } 308 309 static 310 { 311 if ( !useClientManagerPooled ) 312 { 313 System.out.println( "http connection pool disabled in wagon http" ); 314 } 315 else 316 { 317 PoolingClientConnectionManager poolingClientConnectionManager = 318 new PoolingClientConnectionManager( createSchemeRegistry() ); 319 int maxPerRoute = 320 Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxPerRoute", "20" ) ); 321 poolingClientConnectionManager.setDefaultMaxPerRoute( maxPerRoute ); 322 int maxTotal = Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxTotal", "40" ) ); 323 poolingClientConnectionManager.setDefaultMaxPerRoute( maxPerRoute ); 324 poolingClientConnectionManager.setMaxTotal( maxTotal ); 325 326 connectionManagerPooled = poolingClientConnectionManager; 327 } 328 } 329 330 /** 331 * disable all host name verification 332 * 333 * @since 2.0 334 */ 335 private static class RelaxedHostNameVerifier 336 implements X509HostnameVerifier 337 { 338 public void verify( String s, SSLSocket sslSocket ) 339 throws IOException 340 { 341 //no op 342 } 343 344 public void verify( String s, X509Certificate x509Certificate ) 345 throws SSLException 346 { 347 //no op 348 } 349 350 public void verify( String s, String[] strings, String[] strings1 ) 351 throws SSLException 352 { 353 //no op 354 } 355 356 public boolean verify( String s, SSLSession sslSession ) 357 { 358 return true; 359 } 360 } 361 362 public ClientConnectionManager getConnectionManager() 363 { 364 if ( !useClientManagerPooled ) 365 { 366 return clientConnectionManager; 367 } 368 return connectionManagerPooled; 369 } 370 371 public static void setConnectionManagerPooled( ClientConnectionManager clientConnectionManager ) 372 { 373 connectionManagerPooled = clientConnectionManager; 374 } 375 376 public static void setUseClientManagerPooled( boolean pooledClientManager ) 377 { 378 useClientManagerPooled = pooledClientManager; 379 } 380 381 /** 382 * @plexus.configuration 383 * @deprecated Use httpConfiguration instead. 384 */ 385 private Properties httpHeaders; 386 387 /** 388 * @since 1.0-beta-6 389 */ 390 private HttpConfiguration httpConfiguration; 391 392 private HttpGet getMethod; 393 394 public void openConnectionInternal() 395 { 396 repository.setUrl( getURL( repository ) ); 397 client = new DefaultHttpClient( getConnectionManager() ); 398 399 // WAGON-273: default the cookie-policy to browser compatible 400 client.getParams().setParameter( ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY ); 401 402 if ( authenticationInfo != null ) 403 { 404 405 String username = authenticationInfo.getUserName(); 406 String password = authenticationInfo.getPassword(); 407 408 if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) ) 409 { 410 Credentials creds = new UsernamePasswordCredentials( username, password ); 411 412 String host = getRepository().getHost(); 413 int port = getRepository().getPort() > -1 ? getRepository().getPort() : AuthScope.ANY_PORT; 414 415 client.getCredentialsProvider().setCredentials( new AuthScope( host, port ), creds ); 416 // preemptive off by default 417 /* 418 AuthCache authCache = new BasicAuthCache(); 419 BasicScheme basicAuth = new BasicScheme(); 420 HttpHost targetHost = 421 new HttpHost( repository.getHost(), repository.getPort(), repository.getProtocol() ); 422 authCache.put( targetHost, basicAuth ); 423 424 localContext = new BasicHttpContext(); 425 localContext.setAttribute( ClientContext.AUTH_CACHE, authCache ); 426 */ 427 } 428 } 429 430 ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() ); 431 if ( proxyInfo != null ) 432 { 433 String proxyUsername = proxyInfo.getUserName(); 434 String proxyPassword = proxyInfo.getPassword(); 435 String proxyHost = proxyInfo.getHost(); 436 int proxyPort = proxyInfo.getPort(); 437 String proxyNtlmHost = proxyInfo.getNtlmHost(); 438 String proxyNtlmDomain = proxyInfo.getNtlmDomain(); 439 if ( proxyHost != null ) 440 { 441 HttpHost proxy = new HttpHost( proxyHost, proxyPort ); 442 443 if ( proxyUsername != null && proxyPassword != null ) 444 { 445 Credentials creds; 446 if ( proxyNtlmHost != null || proxyNtlmDomain != null ) 447 { 448 creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain ); 449 } 450 else 451 { 452 creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword ); 453 } 454 455 int port = proxyInfo.getPort() > -1 ? proxyInfo.getPort() : AuthScope.ANY_PORT; 456 457 AuthScope authScope = new AuthScope( proxyHost, port ); 458 client.getCredentialsProvider().setCredentials( authScope, creds ); 459 } 460 461 client.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, proxy ); 462 } 463 } 464 } 465 466 public void closeConnection() 467 { 468 if ( !useClientManagerPooled ) 469 { 470 getConnectionManager().shutdown(); 471 } 472 } 473 474 public void put( File source, String resourceName ) 475 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException 476 { 477 Resource resource = new Resource( resourceName ); 478 479 firePutInitiated( resource, source ); 480 481 resource.setContentLength( source.length() ); 482 483 resource.setLastModified( source.lastModified() ); 484 485 put( null, resource, source ); 486 } 487 488 public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified ) 489 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException 490 { 491 Resource resource = new Resource( destination ); 492 493 firePutInitiated( resource, null ); 494 495 resource.setContentLength( contentLength ); 496 497 resource.setLastModified( lastModified ); 498 499 put( stream, resource, null ); 500 } 501 502 private void put( final InputStream stream, Resource resource, File source ) 503 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException 504 { 505 put( resource, source, new RequestEntityImplementation( stream, resource, this, source ) ); 506 } 507 508 private void put( Resource resource, File source, HttpEntity httpEntity ) 509 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException 510 { 511 512 StringBuilder url = new StringBuilder( getURL( getRepository() ) ); 513 String[] parts = StringUtils.split( resource.getName(), "/" ); 514 for ( String part : parts ) 515 { 516 // TODO: Fix encoding... 517 // url += "/" + URLEncoder.encode( parts[i], System.getProperty("file.encoding") ); 518 if ( !url.toString().endsWith( "/" ) ) 519 { 520 url.append( '/' ); 521 } 522 url.append( URLEncoder.encode( part ) ); 523 } 524 put( resource, source, httpEntity, url.toString() ); 525 } 526 527 private void put( Resource resource, File source, HttpEntity httpEntity, String url ) 528 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException 529 { 530 531 //Parent directories need to be created before posting 532 try 533 { 534 mkdirs( PathUtils.dirname( resource.getName() ) ); 535 } 536 catch ( HttpException he ) 537 { 538 fireTransferError( resource, he, TransferEvent.REQUEST_GET ); 539 } 540 catch ( IOException e ) 541 { 542 fireTransferError( resource, e, TransferEvent.REQUEST_GET ); 543 } 544 545 if ( authenticationInfo != null ) 546 { 547 String username = authenticationInfo.getUserName(); 548 String password = authenticationInfo.getPassword(); 549 // preemptive for put 550 if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) ) 551 { 552 AuthCache authCache = new BasicAuthCache(); 553 BasicScheme basicAuth = new BasicScheme(); 554 HttpHost targetHost = 555 new HttpHost( repository.getHost(), repository.getPort(), repository.getProtocol() ); 556 authCache.put( targetHost, basicAuth ); 557 558 localContext = new BasicHttpContext(); 559 localContext.setAttribute( ClientContext.AUTH_CACHE, authCache ); 560 } 561 } 562 563 HttpPut putMethod = new HttpPut( url ); 564 565 firePutStarted( resource, source ); 566 567 try 568 { 569 putMethod.setEntity( httpEntity ); 570 571 HttpResponse response; 572 try 573 { 574 response = execute( putMethod ); 575 } 576 catch ( IOException e ) 577 { 578 fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); 579 580 throw new TransferFailedException( e.getMessage(), e ); 581 } 582 catch ( HttpException e ) 583 { 584 fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); 585 586 throw new TransferFailedException( e.getMessage(), e ); 587 } 588 589 int statusCode = response.getStatusLine().getStatusCode(); 590 String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + "."; 591 fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase ); 592 593 // Check that we didn't run out of retries. 594 switch ( statusCode ) 595 { 596 // Success Codes 597 case HttpStatus.SC_OK: // 200 598 case HttpStatus.SC_CREATED: // 201 599 case HttpStatus.SC_ACCEPTED: // 202 600 case HttpStatus.SC_NO_CONTENT: // 204 601 break; 602 // handle all redirect even if http specs says " the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user" 603 case HttpStatus.SC_MOVED_PERMANENTLY: // 301 604 case HttpStatus.SC_MOVED_TEMPORARILY: // 302 605 case HttpStatus.SC_SEE_OTHER: // 303 606 put( resource, source, httpEntity, calculateRelocatedUrl( response ) ); 607 return; 608 case SC_NULL: 609 { 610 TransferFailedException e = 611 new TransferFailedException( "Failed to transfer file: " + url + reasonPhrase ); 612 fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); 613 throw e; 614 } 615 616 case HttpStatus.SC_FORBIDDEN: 617 fireSessionConnectionRefused(); 618 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase ); 619 620 case HttpStatus.SC_NOT_FOUND: 621 throw new ResourceDoesNotExistException( "File: " + url + " does not exist" + reasonPhrase ); 622 623 //add more entries here 624 default: 625 { 626 TransferFailedException e = new TransferFailedException( 627 "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase ); 628 fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); 629 throw e; 630 } 631 } 632 633 firePutCompleted( resource, source ); 634 } 635 finally 636 { 637 putMethod.abort(); 638 } 639 } 640 641 protected String calculateRelocatedUrl( HttpResponse response ) 642 { 643 Header locationHeader = response.getFirstHeader( "Location" ); 644 String locationField = locationHeader.getValue(); 645 // is it a relative Location or a full ? 646 return locationField.startsWith( "http" ) ? locationField : getURL( getRepository() ) + '/' + locationField; 647 } 648 649 protected void mkdirs( String dirname ) 650 throws HttpException, IOException 651 { 652 // nothing to do 653 } 654 655 public boolean resourceExists( String resourceName ) 656 throws TransferFailedException, AuthorizationException 657 { 658 String repositoryUrl = getRepository().getUrl(); 659 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resourceName; 660 HttpHead headMethod = new HttpHead( url ); 661 HttpResponse response = null; 662 int statusCode; 663 try 664 { 665 response = execute( headMethod ); 666 } 667 catch ( IOException e ) 668 { 669 throw new TransferFailedException( e.getMessage(), e ); 670 } 671 catch ( HttpException e ) 672 { 673 throw new TransferFailedException( e.getMessage(), e ); 674 } 675 676 try 677 { 678 statusCode = response.getStatusLine().getStatusCode(); 679 String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + "."; 680 switch ( statusCode ) 681 { 682 case HttpStatus.SC_OK: 683 return true; 684 685 case HttpStatus.SC_NOT_MODIFIED: 686 return true; 687 688 case SC_NULL: 689 throw new TransferFailedException( "Failed to transfer file: " + url + reasonPhrase ); 690 691 case HttpStatus.SC_FORBIDDEN: 692 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase ); 693 694 case HttpStatus.SC_UNAUTHORIZED: 695 throw new AuthorizationException( "Not authorized " + reasonPhrase ); 696 697 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: 698 throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase ); 699 700 case HttpStatus.SC_NOT_FOUND: 701 return false; 702 703 //add more entries here 704 default: 705 throw new TransferFailedException( 706 "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase ); 707 } 708 } 709 finally 710 { 711 headMethod.abort(); 712 } 713 } 714 715 protected HttpResponse execute( HttpUriRequest httpMethod ) 716 throws HttpException, IOException 717 { 718 setParameters( httpMethod ); 719 setHeaders( httpMethod ); 720 client.getParams().setParameter( CoreProtocolPNames.USER_AGENT, getUserAgent( httpMethod ) ); 721 722 ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() ); 723 724 if ( proxyInfo != null ) 725 { 726 if ( proxyInfo.getUserName() != null && proxyInfo.getPassword() != null ) 727 { 728 Credentials creds; 729 if ( proxyInfo.getNtlmHost() != null || proxyInfo.getNtlmDomain() != null ) 730 { 731 creds = 732 new NTCredentials( proxyInfo.getUserName(), proxyInfo.getPassword(), proxyInfo.getNtlmHost(), 733 proxyInfo.getNtlmDomain() ); 734 } 735 else 736 { 737 creds = new UsernamePasswordCredentials( proxyInfo.getUserName(), proxyInfo.getPassword() ); 738 } 739 740 Header bs = new BasicScheme().authenticate( creds, httpMethod ); 741 httpMethod.addHeader( "Proxy-Authorization", bs.getValue() ); 742 } 743 744 } 745 746 return client.execute( httpMethod, localContext ); 747 } 748 749 protected void setParameters( HttpUriRequest method ) 750 { 751 HttpMethodConfiguration config = 752 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method ); 753 if ( config != null ) 754 { 755 HttpParams params = config.asMethodParams( method.getParams() ); 756 757 if ( config.isUsePreemptive() && authenticationInfo != null ) 758 { 759 String username = authenticationInfo.getUserName(); 760 String password = authenticationInfo.getPassword(); 761 762 if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) ) 763 { 764 765 AuthCache authCache = new BasicAuthCache(); 766 BasicScheme basicAuth = new BasicScheme(); 767 HttpHost targetHost = 768 new HttpHost( repository.getHost(), repository.getPort(), repository.getProtocol() ); 769 authCache.put( targetHost, basicAuth ); 770 771 localContext = new BasicHttpContext(); 772 localContext.setAttribute( ClientContext.AUTH_CACHE, authCache ); 773 } 774 775 } 776 777 if ( params != null ) 778 { 779 method.setParams( params ); 780 } 781 } 782 783 if ( config == null ) 784 { 785 int readTimeout = getReadTimeout(); 786 method.getParams().setParameter( CoreConnectionPNames.SO_TIMEOUT, readTimeout ); 787 } 788 } 789 790 protected void setHeaders( HttpUriRequest method ) 791 { 792 HttpMethodConfiguration config = 793 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method ); 794 if ( config == null || config.isUseDefaultHeaders() ) 795 { 796 // TODO: merge with the other headers and have some better defaults, unify with lightweight headers 797 method.addHeader( "Cache-control", "no-cache" ); 798 method.addHeader( "Cache-store", "no-store" ); 799 method.addHeader( "Pragma", "no-cache" ); 800 method.addHeader( "Expires", "0" ); 801 method.addHeader( "Accept-Encoding", "gzip" ); 802 } 803 804 if ( httpHeaders != null ) 805 { 806 for ( Map.Entry<Object, Object> entry : httpHeaders.entrySet() ) 807 { 808 method.addHeader( (String) entry.getKey(), (String) entry.getValue() ); 809 } 810 } 811 812 Header[] headers = config == null ? null : config.asRequestHeaders(); 813 if ( headers != null ) 814 { 815 for ( int i = 0; i < headers.length; i++ ) 816 { 817 method.addHeader( headers[i] ); 818 } 819 } 820 } 821 822 protected String getUserAgent( HttpUriRequest method ) 823 { 824 if ( httpHeaders != null ) 825 { 826 String value = (String) httpHeaders.get( "User-Agent" ); 827 if ( value != null ) 828 { 829 return value; 830 } 831 } 832 HttpMethodConfiguration config = 833 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method ); 834 835 if ( config != null ) 836 { 837 return (String) config.getHeaders().get( "User-Agent" ); 838 } 839 return null; 840 } 841 842 /** 843 * getUrl 844 * Implementors can override this to remove unwanted parts of the url such as role-hints 845 * 846 * @param repository 847 * @return 848 */ 849 protected String getURL( Repository repository ) 850 { 851 return repository.getUrl(); 852 } 853 854 public HttpConfiguration getHttpConfiguration() 855 { 856 return httpConfiguration; 857 } 858 859 public void setHttpConfiguration( HttpConfiguration httpConfiguration ) 860 { 861 this.httpConfiguration = httpConfiguration; 862 } 863 864 public void fillInputData( InputData inputData ) 865 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException 866 { 867 Resource resource = inputData.getResource(); 868 869 String repositoryUrl = getRepository().getUrl(); 870 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resource.getName(); 871 getMethod = new HttpGet( url ); 872 long timestamp = resource.getLastModified(); 873 if ( timestamp > 0 ) 874 { 875 SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US ); 876 fmt.setTimeZone( GMT_TIME_ZONE ); 877 Header hdr = new BasicHeader( "If-Modified-Since", fmt.format( new Date( timestamp ) ) ); 878 fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" ); 879 getMethod.addHeader( hdr ); 880 } 881 882 HttpResponse response; 883 int statusCode; 884 try 885 { 886 response = execute( getMethod ); 887 } 888 catch ( IOException e ) 889 { 890 fireTransferError( resource, e, TransferEvent.REQUEST_GET ); 891 892 throw new TransferFailedException( e.getMessage(), e ); 893 } 894 catch ( HttpException e ) 895 { 896 fireTransferError( resource, e, TransferEvent.REQUEST_GET ); 897 898 throw new TransferFailedException( e.getMessage(), e ); 899 } 900 901 statusCode = response.getStatusLine().getStatusCode(); 902 903 String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + "."; 904 905 fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase ); 906 907 // TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is 908 // required 909 switch ( statusCode ) 910 { 911 case HttpStatus.SC_OK: 912 break; 913 914 case HttpStatus.SC_NOT_MODIFIED: 915 // return, leaving last modified set to original value so getIfNewer should return unmodified 916 return; 917 918 case SC_NULL: 919 { 920 TransferFailedException e = 921 new TransferFailedException( "Failed to transfer file: " + url + " " + reasonPhrase ); 922 fireTransferError( resource, e, TransferEvent.REQUEST_GET ); 923 throw e; 924 } 925 926 case HttpStatus.SC_FORBIDDEN: 927 fireSessionConnectionRefused(); 928 throw new AuthorizationException( "Access denied to: " + url + " " + reasonPhrase ); 929 930 case HttpStatus.SC_UNAUTHORIZED: 931 fireSessionConnectionRefused(); 932 throw new AuthorizationException( "Not authorized " + reasonPhrase ); 933 934 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: 935 fireSessionConnectionRefused(); 936 throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase ); 937 938 case HttpStatus.SC_NOT_FOUND: 939 throw new ResourceDoesNotExistException( "File: " + url + " " + reasonPhrase ); 940 941 // add more entries here 942 default: 943 { 944 cleanupGetTransfer( resource ); 945 TransferFailedException e = new TransferFailedException( 946 "Failed to transfer file: " + url + ". Return code is: " + statusCode + " " + reasonPhrase ); 947 fireTransferError( resource, e, TransferEvent.REQUEST_GET ); 948 throw e; 949 } 950 } 951 952 InputStream is; 953 954 Header contentLengthHeader = response.getFirstHeader( "Content-Length" ); 955 956 if ( contentLengthHeader != null ) 957 { 958 try 959 { 960 long contentLength = Long.parseLong( contentLengthHeader.getValue() ); 961 962 resource.setContentLength( contentLength ); 963 } 964 catch ( NumberFormatException e ) 965 { 966 fireTransferDebug( 967 "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e ); 968 } 969 } 970 971 Header lastModifiedHeader = response.getFirstHeader( "Last-Modified" ); 972 973 long lastModified = 0; 974 975 if ( lastModifiedHeader != null ) 976 { 977 try 978 { 979 lastModified = DateUtils.parseDate( lastModifiedHeader.getValue() ).getTime(); 980 981 resource.setLastModified( lastModified ); 982 } 983 catch ( DateParseException e ) 984 { 985 fireTransferDebug( "Unable to parse last modified header" ); 986 } 987 988 fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" ); 989 } 990 991 Header contentEncoding = response.getFirstHeader( "Content-Encoding" ); 992 boolean isGZipped = contentEncoding == null ? false : "gzip".equalsIgnoreCase( contentEncoding.getValue() ); 993 994 try 995 { 996 is = response.getEntity().getContent(); 997 998 if ( isGZipped ) 999 { 1000 is = new GZIPInputStream( is ); 1001 } 1002 } 1003 catch ( IOException e ) 1004 { 1005 fireTransferError( resource, e, TransferEvent.REQUEST_GET ); 1006 1007 String msg = 1008 "Error occurred while retrieving from remote repository " + getRepository() + ": " + e.getMessage(); 1009 1010 throw new TransferFailedException( msg, e ); 1011 } 1012 1013 inputData.setInputStream( is ); 1014 } 1015 1016 protected void cleanupGetTransfer( Resource resource ) 1017 { 1018 if ( getMethod != null ) 1019 { 1020 getMethod.abort(); 1021 } 1022 } 1023 1024 1025 @Override 1026 public void putFromStream( InputStream stream, String destination ) 1027 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException 1028 { 1029 putFromStream( stream, destination, -1, -1 ); 1030 } 1031 1032 @Override 1033 protected void putFromStream( InputStream stream, Resource resource ) 1034 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException 1035 { 1036 putFromStream( stream, resource.getName(), -1, -1 ); 1037 } 1038 1039 public Properties getHttpHeaders() 1040 { 1041 return httpHeaders; 1042 } 1043 1044 public void setHttpHeaders( Properties httpHeaders ) 1045 { 1046 this.httpHeaders = httpHeaders; 1047 } 1048 1049 @Override 1050 public void fillOutputData( OutputData outputData ) 1051 throws TransferFailedException 1052 { 1053 // no needed in this implementation but throw an Exception if used 1054 throw new IllegalStateException( "this wagon http client must not use fillOutputData" ); 1055 } 1056}