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