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