1 package org.apache.maven.wagon.providers.http;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.http.Header;
23 import org.apache.http.HttpEntity;
24 import org.apache.http.HttpException;
25 import org.apache.http.HttpHost;
26 import org.apache.http.HttpResponse;
27 import org.apache.http.HttpStatus;
28 import org.apache.http.auth.AUTH;
29 import org.apache.http.auth.AuthScope;
30 import org.apache.http.auth.Credentials;
31 import org.apache.http.auth.MalformedChallengeException;
32 import org.apache.http.auth.NTCredentials;
33 import org.apache.http.auth.UsernamePasswordCredentials;
34 import org.apache.http.client.AuthCache;
35 import org.apache.http.client.CredentialsProvider;
36 import org.apache.http.client.config.CookieSpecs;
37 import org.apache.http.client.config.RequestConfig;
38 import org.apache.http.client.methods.CloseableHttpResponse;
39 import org.apache.http.client.methods.HttpGet;
40 import org.apache.http.client.methods.HttpHead;
41 import org.apache.http.client.methods.HttpPut;
42 import org.apache.http.client.methods.HttpUriRequest;
43 import org.apache.http.client.protocol.HttpClientContext;
44 import org.apache.http.client.utils.DateUtils;
45 import org.apache.http.config.Registry;
46 import org.apache.http.config.RegistryBuilder;
47 import org.apache.http.conn.HttpClientConnectionManager;
48 import org.apache.http.conn.socket.ConnectionSocketFactory;
49 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
50 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
51 import org.apache.http.conn.ssl.SSLContextBuilder;
52 import org.apache.http.conn.ssl.SSLInitializationException;
53 import org.apache.http.entity.AbstractHttpEntity;
54 import org.apache.http.impl.auth.BasicScheme;
55 import org.apache.http.impl.client.BasicAuthCache;
56 import org.apache.http.impl.client.BasicCredentialsProvider;
57 import org.apache.http.impl.client.CloseableHttpClient;
58 import org.apache.http.impl.client.HttpClientBuilder;
59 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
60 import org.apache.http.message.BasicHeader;
61 import org.apache.http.protocol.HTTP;
62 import org.apache.http.util.EntityUtils;
63 import org.apache.maven.wagon.InputData;
64 import org.apache.maven.wagon.OutputData;
65 import org.apache.maven.wagon.PathUtils;
66 import org.apache.maven.wagon.ResourceDoesNotExistException;
67 import org.apache.maven.wagon.StreamWagon;
68 import org.apache.maven.wagon.TransferFailedException;
69 import org.apache.maven.wagon.Wagon;
70 import org.apache.maven.wagon.authorization.AuthorizationException;
71 import org.apache.maven.wagon.events.TransferEvent;
72 import org.apache.maven.wagon.proxy.ProxyInfo;
73 import org.apache.maven.wagon.repository.Repository;
74 import org.apache.maven.wagon.resource.Resource;
75 import org.apache.maven.wagon.shared.http.EncodingUtil;
76 import org.codehaus.plexus.util.IOUtil;
77 import org.codehaus.plexus.util.StringUtils;
78
79 import javax.net.ssl.HttpsURLConnection;
80 import javax.net.ssl.SSLContext;
81 import java.io.ByteArrayInputStream;
82 import java.io.Closeable;
83 import java.io.File;
84 import java.io.FileInputStream;
85 import java.io.IOException;
86 import java.io.InputStream;
87 import java.io.OutputStream;
88 import java.nio.ByteBuffer;
89 import java.text.SimpleDateFormat;
90 import java.util.Date;
91 import java.util.Locale;
92 import java.util.Map;
93 import java.util.Properties;
94 import java.util.TimeZone;
95 import java.util.concurrent.TimeUnit;
96
97
98
99
100
101 public abstract class AbstractHttpClientWagon
102 extends StreamWagon
103 {
104 private final class RequestEntityImplementation
105 extends AbstractHttpEntity
106 {
107
108 private static final int BUFFER_SIZE = 2048;
109
110 private final Resource resource;
111
112 private final Wagon wagon;
113
114 private ByteBuffer byteBuffer;
115
116 private File source;
117
118 private long length = -1;
119
120 private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon,
121 final File source )
122 throws TransferFailedException
123 {
124 if ( source != null )
125 {
126 this.source = source;
127 }
128 else
129 {
130 try
131 {
132 byte[] bytes = IOUtil.toByteArray( stream );
133 byteBuffer = ByteBuffer.allocate( bytes.length );
134 byteBuffer.put( bytes );
135 }
136 catch ( IOException e )
137 {
138 throw new TransferFailedException( e.getMessage(), e );
139 }
140 }
141 this.resource = resource;
142 this.length = resource == null ? -1 : resource.getContentLength();
143
144 this.wagon = wagon;
145 }
146
147 public long getContentLength()
148 {
149 return length;
150 }
151
152 public InputStream getContent()
153 throws IOException, IllegalStateException
154 {
155 if ( this.source != null )
156 {
157 return new FileInputStream( this.source );
158 }
159 return new ByteArrayInputStream( this.byteBuffer.array() );
160 }
161
162 public boolean isRepeatable()
163 {
164 return true;
165 }
166
167 public void writeTo( final OutputStream outstream )
168 throws IOException
169 {
170 if ( outstream == null )
171 {
172 throw new IllegalArgumentException( "Output stream may not be null" );
173 }
174 TransferEvent transferEvent =
175 new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
176 transferEvent.setTimestamp( System.currentTimeMillis() );
177 InputStream instream = ( this.source != null )
178 ? new FileInputStream( this.source )
179 : new ByteArrayInputStream( this.byteBuffer.array() );
180 try
181 {
182 byte[] buffer = new byte[BUFFER_SIZE];
183 int l;
184 if ( this.length < 0 )
185 {
186
187 while ( ( l = instream.read( buffer ) ) != -1 )
188 {
189 fireTransferProgress( transferEvent, buffer, -1 );
190 outstream.write( buffer, 0, l );
191 }
192 }
193 else
194 {
195
196 long remaining = this.length;
197 while ( remaining > 0 )
198 {
199 l = instream.read( buffer, 0, (int) Math.min( BUFFER_SIZE, remaining ) );
200 if ( l == -1 )
201 {
202 break;
203 }
204 fireTransferProgress( transferEvent, buffer, (int) Math.min( BUFFER_SIZE, remaining ) );
205 outstream.write( buffer, 0, l );
206 remaining -= l;
207 }
208 }
209 }
210 finally
211 {
212 instream.close();
213 }
214 }
215
216 public boolean isStreaming()
217 {
218 return true;
219 }
220 }
221
222 private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
223
224
225
226
227
228 private static boolean persistentPool =
229 Boolean.valueOf( System.getProperty( "maven.wagon.http.pool", "true" ) );
230
231
232
233
234
235 private static final boolean SSL_INSECURE =
236 Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.insecure", "false" ) );
237
238
239
240
241
242 private static final boolean IGNORE_SSL_VALIDITY_DATES =
243 Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.ignore.validity.dates", "false" ) );
244
245
246
247
248
249 private static final boolean SSL_ALLOW_ALL =
250 Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.allowall", "false" ) );
251
252
253
254
255
256
257 private static final int MAX_CONN_PER_ROUTE =
258 Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxPerRoute", "20" ) );
259
260
261
262
263
264 private static final int MAX_CONN_TOTAL =
265 Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxTotal", "40" ) );
266
267
268
269
270 private static HttpClientConnectionManager httpClientConnectionManager = createConnManager();
271
272
273
274
275
276 protected static final int SC_TOO_MANY_REQUESTS = 429;
277
278
279
280
281
282
283
284
285
286
287
288
289 private int initialBackoffSeconds =
290 Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.backoffSeconds", "5" ) );
291
292
293
294
295
296
297
298 private static final int MAX_BACKOFF_WAIT_SECONDS =
299 Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxBackoffSeconds", "180" ) );
300
301
302 protected int backoff( int wait, String url )
303 throws InterruptedException, TransferFailedException
304 {
305 TimeUnit.SECONDS.sleep( wait );
306 int nextWait = wait * 2;
307 if ( nextWait >= getMaxBackoffWaitSeconds() )
308 {
309 throw new TransferFailedException(
310 "Waited too long to access: " + url + ". Return code is: " + SC_TOO_MANY_REQUESTS );
311 }
312 return nextWait;
313 }
314
315 @SuppressWarnings( "checkstyle:linelength" )
316 private static PoolingHttpClientConnectionManager createConnManager()
317 {
318
319 String sslProtocolsStr = System.getProperty( "https.protocols" );
320 String cipherSuitesStr = System.getProperty( "https.cipherSuites" );
321 String[] sslProtocols = sslProtocolsStr != null ? sslProtocolsStr.split( " *, *" ) : null;
322 String[] cipherSuites = cipherSuitesStr != null ? cipherSuitesStr.split( " *, *" ) : null;
323
324 SSLConnectionSocketFactory sslConnectionSocketFactory;
325 if ( SSL_INSECURE )
326 {
327 try
328 {
329 SSLContext sslContext = new SSLContextBuilder().useSSL().loadTrustMaterial( null,
330 new RelaxedTrustStrategy(
331 IGNORE_SSL_VALIDITY_DATES ) ).build();
332 sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslContext, sslProtocols, cipherSuites,
333 SSL_ALLOW_ALL
334 ? SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
335 : SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER );
336 }
337 catch ( Exception ex )
338 {
339 throw new SSLInitializationException( ex.getMessage(), ex );
340 }
341 }
342 else
343 {
344 sslConnectionSocketFactory =
345 new SSLConnectionSocketFactory( HttpsURLConnection.getDefaultSSLSocketFactory(), sslProtocols,
346 cipherSuites,
347 SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER );
348 }
349
350 Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register( "http",
351 PlainConnectionSocketFactory.INSTANCE ).register(
352 "https", sslConnectionSocketFactory ).build();
353
354 PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( registry );
355 if ( persistentPool )
356 {
357 connManager.setDefaultMaxPerRoute( MAX_CONN_PER_ROUTE );
358 connManager.setMaxTotal( MAX_CONN_TOTAL );
359 }
360 else
361 {
362 connManager.setMaxTotal( 1 );
363 }
364 return connManager;
365 }
366
367 private static final CloseableHttpClient CLIENT = createClient();
368
369 private static CloseableHttpClient createClient()
370 {
371 return HttpClientBuilder.create()
372 .useSystemProperties()
373 .disableConnectionState()
374 .setConnectionManager( httpClientConnectionManager )
375 .build();
376 }
377
378 private CredentialsProvider credentialsProvider;
379
380 private AuthCache authCache;
381
382 private HttpClientContext localContext;
383
384 private Closeable closeable;
385
386
387
388
389
390 private Properties httpHeaders;
391
392
393
394
395 private HttpConfiguration httpConfiguration;
396
397
398
399
400
401 private BasicAuthScope basicAuth;
402
403
404
405
406
407 private BasicAuthScope proxyAuth;
408
409 public void openConnectionInternal()
410 {
411 repository.setUrl( getURL( repository ) );
412
413 localContext = HttpClientContext.create();
414 credentialsProvider = new BasicCredentialsProvider();
415 authCache = new BasicAuthCache();
416 localContext.setCredentialsProvider( credentialsProvider );
417 localContext.setAuthCache( authCache );
418
419 if ( authenticationInfo != null )
420 {
421
422 String username = authenticationInfo.getUserName();
423 String password = authenticationInfo.getPassword();
424
425 if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) )
426 {
427 Credentials creds = new UsernamePasswordCredentials( username, password );
428
429 String host = getRepository().getHost();
430 int port = getRepository().getPort();
431
432 credentialsProvider.setCredentials( getBasicAuthScope().getScope( host, port ), creds );
433 }
434 }
435
436 ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
437 if ( proxyInfo != null )
438 {
439 String proxyUsername = proxyInfo.getUserName();
440 String proxyPassword = proxyInfo.getPassword();
441 String proxyHost = proxyInfo.getHost();
442 String proxyNtlmHost = proxyInfo.getNtlmHost();
443 String proxyNtlmDomain = proxyInfo.getNtlmDomain();
444 if ( proxyHost != null )
445 {
446 if ( proxyUsername != null && proxyPassword != null )
447 {
448 Credentials creds;
449 if ( proxyNtlmHost != null || proxyNtlmDomain != null )
450 {
451 creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
452 }
453 else
454 {
455 creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
456 }
457
458 int port = proxyInfo.getPort();
459
460 AuthScope authScope = getProxyBasicAuthScope().getScope( proxyHost, port );
461 credentialsProvider.setCredentials( authScope, creds );
462 }
463 }
464 }
465 }
466
467 public void closeConnection()
468 {
469 if ( !persistentPool )
470 {
471 httpClientConnectionManager.closeIdleConnections( 0, TimeUnit.MILLISECONDS );
472 }
473 }
474
475 public static void setPersistentPool( boolean persistentPool )
476 {
477 persistentPool = persistentPool;
478 }
479
480 public static void setPoolingHttpClientConnectionManager(
481 PoolingHttpClientConnectionManager poolingHttpClientConnectionManager )
482 {
483 httpClientConnectionManager = poolingHttpClientConnectionManager;
484 }
485
486 public void put( File source, String resourceName )
487 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
488 {
489 Resource resource = new Resource( resourceName );
490
491 firePutInitiated( resource, source );
492
493 resource.setContentLength( source.length() );
494
495 resource.setLastModified( source.lastModified() );
496
497 put( null, resource, source );
498 }
499
500 public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
501 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
502 {
503 Resource resource = new Resource( destination );
504
505 firePutInitiated( resource, null );
506
507 resource.setContentLength( contentLength );
508
509 resource.setLastModified( lastModified );
510
511 put( stream, resource, null );
512 }
513
514 private void put( final InputStream stream, Resource resource, File source )
515 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
516 {
517 put( resource, source, new RequestEntityImplementation( stream, resource, this, source ) );
518 }
519
520 private void put( Resource resource, File source, HttpEntity httpEntity )
521 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
522 {
523 put( resource, source, httpEntity, buildUrl( resource ) );
524 }
525
526
527
528
529
530
531
532 private String buildUrl( Resource resource )
533 {
534 return EncodingUtil.encodeURLToString( getRepository().getUrl(), resource.getName() );
535 }
536
537
538 private void put( Resource resource, File source, HttpEntity httpEntity, String url )
539 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
540 {
541 put( getInitialBackoffSeconds(), resource, source, httpEntity, url );
542 }
543
544
545 private void put( int wait, Resource resource, File source, HttpEntity httpEntity, String url )
546 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
547 {
548
549
550 try
551 {
552 mkdirs( PathUtils.dirname( resource.getName() ) );
553 }
554 catch ( HttpException he )
555 {
556 fireTransferError( resource, he, TransferEvent.REQUEST_GET );
557 }
558 catch ( IOException e )
559 {
560 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
561 }
562
563
564
565
566 Repository repo = getRepository();
567 HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() );
568 AuthScope targetScope = getBasicAuthScope().getScope( targetHost );
569
570 if ( credentialsProvider.getCredentials( targetScope ) != null )
571 {
572 BasicScheme targetAuth = new BasicScheme();
573 try
574 {
575 targetAuth.processChallenge( new BasicHeader( AUTH.WWW_AUTH, "BASIC preemptive" ) );
576 authCache.put( targetHost, targetAuth );
577 }
578 catch ( MalformedChallengeException ignore )
579 {
580
581 }
582 }
583
584 HttpPut putMethod = new HttpPut( url );
585
586 firePutStarted( resource, source );
587
588 try
589 {
590 putMethod.setEntity( httpEntity );
591
592 CloseableHttpResponse response = execute( putMethod );
593 try
594 {
595 int statusCode = response.getStatusLine().getStatusCode();
596 String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + ".";
597 fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
598
599
600 switch ( statusCode )
601 {
602
603 case HttpStatus.SC_OK:
604 case HttpStatus.SC_CREATED:
605 case HttpStatus.SC_ACCEPTED:
606 case HttpStatus.SC_NO_CONTENT:
607 break;
608
609
610 case HttpStatus.SC_MOVED_PERMANENTLY:
611 case HttpStatus.SC_MOVED_TEMPORARILY:
612 case HttpStatus.SC_SEE_OTHER:
613 put( resource, source, httpEntity, calculateRelocatedUrl( response ) );
614 return;
615 case HttpStatus.SC_FORBIDDEN:
616 fireSessionConnectionRefused();
617 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
618
619 case HttpStatus.SC_NOT_FOUND:
620 throw new ResourceDoesNotExistException( "File: " + url + " does not exist" + reasonPhrase );
621
622 case SC_TOO_MANY_REQUESTS:
623 put( backoff( wait, url ), resource, source, httpEntity, url );
624 break;
625
626 default:
627 TransferFailedException e = new TransferFailedException(
628 "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
629 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
630 throw e;
631 }
632
633 firePutCompleted( resource, source );
634
635 EntityUtils.consume( response.getEntity() );
636 }
637 finally
638 {
639 response.close();
640 }
641 }
642 catch ( IOException e )
643 {
644 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
645
646 throw new TransferFailedException( e.getMessage(), e );
647 }
648 catch ( HttpException e )
649 {
650 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
651
652 throw new TransferFailedException( e.getMessage(), e );
653 }
654 catch ( InterruptedException e )
655 {
656 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
657
658 throw new TransferFailedException( e.getMessage(), e );
659 }
660
661 }
662
663 protected String calculateRelocatedUrl( HttpResponse response )
664 {
665 Header locationHeader = response.getFirstHeader( "Location" );
666 String locationField = locationHeader.getValue();
667
668 return locationField.startsWith( "http" ) ? locationField : getURL( getRepository() ) + '/' + locationField;
669 }
670
671 protected void mkdirs( String dirname )
672 throws HttpException, IOException
673 {
674
675 }
676
677 public boolean resourceExists( String resourceName )
678 throws TransferFailedException, AuthorizationException
679 {
680 return resourceExists( getInitialBackoffSeconds(), resourceName );
681 }
682
683
684 private boolean resourceExists( int wait, String resourceName )
685 throws TransferFailedException, AuthorizationException
686 {
687 String repositoryUrl = getRepository().getUrl();
688 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resourceName;
689 HttpHead headMethod = new HttpHead( url );
690 try
691 {
692 CloseableHttpResponse response = execute( headMethod );
693 try
694 {
695 int statusCode = response.getStatusLine().getStatusCode();
696 String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + ".";
697 boolean result;
698 switch ( statusCode )
699 {
700 case HttpStatus.SC_OK:
701 result = true;
702 break;
703 case HttpStatus.SC_NOT_MODIFIED:
704 result = true;
705 break;
706 case HttpStatus.SC_FORBIDDEN:
707 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
708
709 case HttpStatus.SC_UNAUTHORIZED:
710 throw new AuthorizationException( "Not authorized " + reasonPhrase );
711
712 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
713 throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase );
714
715 case HttpStatus.SC_NOT_FOUND:
716 result = false;
717 break;
718
719 case SC_TOO_MANY_REQUESTS:
720 return resourceExists( backoff( wait, resourceName ), resourceName );
721
722
723 default:
724 throw new TransferFailedException(
725 "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
726 }
727
728 EntityUtils.consume( response.getEntity() );
729 return result;
730 }
731 finally
732 {
733 response.close();
734 }
735 }
736 catch ( IOException e )
737 {
738 throw new TransferFailedException( e.getMessage(), e );
739 }
740 catch ( HttpException e )
741 {
742 throw new TransferFailedException( e.getMessage(), e );
743 }
744 catch ( InterruptedException e )
745 {
746 throw new TransferFailedException( e.getMessage(), e );
747 }
748
749 }
750
751 protected CloseableHttpResponse execute( HttpUriRequest httpMethod )
752 throws HttpException, IOException
753 {
754 setHeaders( httpMethod );
755 String userAgent = getUserAgent( httpMethod );
756 if ( userAgent != null )
757 {
758 httpMethod.setHeader( HTTP.USER_AGENT, userAgent );
759 }
760
761 RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
762
763 requestConfigBuilder.setCookieSpec( CookieSpecs.BROWSER_COMPATIBILITY );
764
765 Repository repo = getRepository();
766 ProxyInfo proxyInfo = getProxyInfo( repo.getProtocol(), repo.getHost() );
767 if ( proxyInfo != null )
768 {
769 HttpHost proxy = new HttpHost( proxyInfo.getHost(), proxyInfo.getPort() );
770 requestConfigBuilder.setProxy( proxy );
771 }
772
773 HttpMethodConfiguration config =
774 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( httpMethod );
775
776 if ( config != null )
777 {
778 ConfigurationUtils.copyConfig( config, requestConfigBuilder );
779 }
780 else
781 {
782 requestConfigBuilder.setSocketTimeout( getReadTimeout() );
783 }
784
785 localContext.setRequestConfig( requestConfigBuilder.build() );
786
787 if ( config != null && config.isUsePreemptive() )
788 {
789 HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() );
790 AuthScope targetScope = getBasicAuthScope().getScope( targetHost );
791
792 if ( credentialsProvider.getCredentials( targetScope ) != null )
793 {
794 BasicScheme targetAuth = new BasicScheme();
795 targetAuth.processChallenge( new BasicHeader( AUTH.WWW_AUTH, "BASIC preemptive" ) );
796 authCache.put( targetHost, targetAuth );
797 }
798 }
799
800 if ( proxyInfo != null )
801 {
802 if ( proxyInfo.getHost() != null )
803 {
804 HttpHost proxyHost = new HttpHost( proxyInfo.getHost(), proxyInfo.getPort() );
805 AuthScope proxyScope = getProxyBasicAuthScope().getScope( proxyHost );
806
807 String proxyUsername = proxyInfo.getUserName();
808 String proxyPassword = proxyInfo.getPassword();
809 String proxyNtlmHost = proxyInfo.getNtlmHost();
810 String proxyNtlmDomain = proxyInfo.getNtlmDomain();
811
812 if ( proxyUsername != null && proxyPassword != null )
813 {
814 Credentials creds;
815 if ( proxyNtlmHost != null || proxyNtlmDomain != null )
816 {
817 creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
818 }
819 else
820 {
821 creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
822 }
823
824 credentialsProvider.setCredentials( proxyScope, creds );
825 BasicScheme proxyAuth = new BasicScheme();
826 proxyAuth.processChallenge( new BasicHeader( AUTH.PROXY_AUTH, "BASIC preemptive" ) );
827 authCache.put( proxyHost, proxyAuth );
828 }
829 }
830 }
831
832 return CLIENT.execute( httpMethod, localContext );
833 }
834
835 protected void setHeaders( HttpUriRequest method )
836 {
837 HttpMethodConfiguration config =
838 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
839 if ( config == null || config.isUseDefaultHeaders() )
840 {
841
842 method.addHeader( "Cache-control", "no-cache" );
843 method.addHeader( "Cache-store", "no-store" );
844 method.addHeader( "Pragma", "no-cache" );
845 method.addHeader( "Expires", "0" );
846 method.addHeader( "Accept-Encoding", "gzip" );
847 }
848
849 if ( httpHeaders != null )
850 {
851 for ( Map.Entry<Object, Object> entry : httpHeaders.entrySet() )
852 {
853 method.setHeader( (String) entry.getKey(), (String) entry.getValue() );
854 }
855 }
856
857 Header[] headers = config == null ? null : config.asRequestHeaders();
858 if ( headers != null )
859 {
860 for ( Header header : headers )
861 {
862 method.setHeader( header );
863 }
864 }
865 }
866
867 protected String getUserAgent( HttpUriRequest method )
868 {
869 if ( httpHeaders != null )
870 {
871 String value = (String) httpHeaders.get( "User-Agent" );
872 if ( value != null )
873 {
874 return value;
875 }
876 }
877 HttpMethodConfiguration config =
878 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
879
880 if ( config != null )
881 {
882 return (String) config.getHeaders().get( "User-Agent" );
883 }
884 return null;
885 }
886
887
888
889
890
891
892
893
894 protected String getURL( Repository repository )
895 {
896 return repository.getUrl();
897 }
898
899 public HttpConfiguration getHttpConfiguration()
900 {
901 return httpConfiguration;
902 }
903
904 public void setHttpConfiguration( HttpConfiguration httpConfiguration )
905 {
906 this.httpConfiguration = httpConfiguration;
907 }
908
909
910
911
912
913
914 public BasicAuthScope getBasicAuthScope()
915 {
916 if ( basicAuth == null )
917 {
918 basicAuth = new BasicAuthScope();
919 }
920 return basicAuth;
921 }
922
923
924
925
926
927
928 public void setBasicAuthScope( BasicAuthScope basicAuth )
929 {
930 this.basicAuth = basicAuth;
931 }
932
933
934
935
936
937
938 public BasicAuthScope getProxyBasicAuthScope()
939 {
940 if ( proxyAuth == null )
941 {
942 proxyAuth = new BasicAuthScope();
943 }
944 return proxyAuth;
945 }
946
947
948
949
950
951
952 public void setProxyBasicAuthScope( BasicAuthScope proxyAuth )
953 {
954 this.proxyAuth = proxyAuth;
955 }
956
957 public void fillInputData( InputData inputData )
958 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
959 {
960 fillInputData( getInitialBackoffSeconds(), inputData );
961 }
962
963 private void fillInputData( int wait, InputData inputData )
964 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
965 {
966 Resource resource = inputData.getResource();
967
968 String repositoryUrl = getRepository().getUrl();
969 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resource.getName();
970 HttpGet getMethod = new HttpGet( url );
971 long timestamp = resource.getLastModified();
972 if ( timestamp > 0 )
973 {
974 SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
975 fmt.setTimeZone( GMT_TIME_ZONE );
976 Header hdr = new BasicHeader( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
977 fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
978 getMethod.addHeader( hdr );
979 }
980
981 try
982 {
983 CloseableHttpResponse response = execute( getMethod );
984 closeable = response;
985 int statusCode = response.getStatusLine().getStatusCode();
986
987 String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + ".";
988
989 fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
990
991 switch ( statusCode )
992 {
993 case HttpStatus.SC_OK:
994 break;
995
996 case HttpStatus.SC_NOT_MODIFIED:
997
998 return;
999 case HttpStatus.SC_FORBIDDEN:
1000 fireSessionConnectionRefused();
1001 throw new AuthorizationException( "Access denied to: " + url + " " + reasonPhrase );
1002
1003 case HttpStatus.SC_UNAUTHORIZED:
1004 fireSessionConnectionRefused();
1005 throw new AuthorizationException( "Not authorized " + reasonPhrase );
1006
1007 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
1008 fireSessionConnectionRefused();
1009 throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase );
1010
1011 case HttpStatus.SC_NOT_FOUND:
1012 throw new ResourceDoesNotExistException( "File: " + url + " " + reasonPhrase );
1013
1014 case SC_TOO_MANY_REQUESTS:
1015 fillInputData( backoff( wait, url ), inputData );
1016 break;
1017
1018
1019 default:
1020 cleanupGetTransfer( resource );
1021 TransferFailedException e = new TransferFailedException(
1022 "Failed to transfer file: " + url + ". Return code is: " + statusCode + " " + reasonPhrase );
1023 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
1024 throw e;
1025 }
1026
1027 Header contentLengthHeader = response.getFirstHeader( "Content-Length" );
1028
1029 if ( contentLengthHeader != null )
1030 {
1031 try
1032 {
1033 long contentLength = Long.parseLong( contentLengthHeader.getValue() );
1034
1035 resource.setContentLength( contentLength );
1036 }
1037 catch ( NumberFormatException e )
1038 {
1039 fireTransferDebug(
1040 "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
1041 }
1042 }
1043
1044 Header lastModifiedHeader = response.getFirstHeader( "Last-Modified" );
1045 if ( lastModifiedHeader != null )
1046 {
1047 Date lastModified = DateUtils.parseDate( lastModifiedHeader.getValue() );
1048 if ( lastModified != null )
1049 {
1050 resource.setLastModified( lastModified.getTime() );
1051 fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " ("
1052 + lastModified.getTime() + ")" );
1053 }
1054 }
1055
1056 HttpEntity entity = response.getEntity();
1057 if ( entity != null )
1058 {
1059 inputData.setInputStream( entity.getContent() );
1060 }
1061 }
1062 catch ( IOException e )
1063 {
1064 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
1065
1066 throw new TransferFailedException( e.getMessage(), e );
1067 }
1068 catch ( HttpException e )
1069 {
1070 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
1071
1072 throw new TransferFailedException( e.getMessage(), e );
1073 }
1074 catch ( InterruptedException e )
1075 {
1076 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
1077
1078 throw new TransferFailedException( e.getMessage(), e );
1079 }
1080
1081 }
1082
1083 protected void cleanupGetTransfer( Resource resource )
1084 {
1085 if ( closeable != null )
1086 {
1087 try
1088 {
1089 closeable.close();
1090 }
1091 catch ( IOException ignore )
1092 {
1093
1094 }
1095
1096 }
1097 }
1098
1099
1100 @Override
1101 public void putFromStream( InputStream stream, String destination )
1102 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
1103 {
1104 putFromStream( stream, destination, -1, -1 );
1105 }
1106
1107 @Override
1108 protected void putFromStream( InputStream stream, Resource resource )
1109 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
1110 {
1111 putFromStream( stream, resource.getName(), -1, -1 );
1112 }
1113
1114 public Properties getHttpHeaders()
1115 {
1116 return httpHeaders;
1117 }
1118
1119 public void setHttpHeaders( Properties httpHeaders )
1120 {
1121 this.httpHeaders = httpHeaders;
1122 }
1123
1124 @Override
1125 public void fillOutputData( OutputData outputData )
1126 throws TransferFailedException
1127 {
1128
1129 throw new IllegalStateException( "this wagon http client must not use fillOutputData" );
1130 }
1131
1132 public int getInitialBackoffSeconds()
1133 {
1134 return initialBackoffSeconds;
1135 }
1136
1137 public void setInitialBackoffSeconds( int initialBackoffSeconds )
1138 {
1139 this.initialBackoffSeconds = initialBackoffSeconds;
1140 }
1141
1142 public static int getMaxBackoffWaitSeconds()
1143 {
1144 return MAX_BACKOFF_WAIT_SECONDS;
1145 }
1146 }