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