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