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