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.codehaus.plexus.util.IOUtil;
75 import org.codehaus.plexus.util.StringUtils;
76
77 import javax.net.ssl.HttpsURLConnection;
78 import javax.net.ssl.SSLContext;
79 import java.io.ByteArrayInputStream;
80 import java.io.Closeable;
81 import java.io.File;
82 import java.io.FileInputStream;
83 import java.io.IOException;
84 import java.io.InputStream;
85 import java.io.OutputStream;
86 import java.net.URLEncoder;
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 private static PoolingHttpClientConnectionManager createConnManager()
272 {
273
274 String sslProtocolsStr = System.getProperty( "https.protocols" );
275 String cipherSuitesStr = System.getProperty( "https.cipherSuites" );
276 String[] sslProtocols = sslProtocolsStr != null ? sslProtocolsStr.split( " *, *" ) : null;
277 String[] cipherSuites = cipherSuitesStr != null ? cipherSuitesStr.split( " *, *" ) : null;
278
279 SSLConnectionSocketFactory sslConnectionSocketFactory;
280 if ( SSL_INSECURE )
281 {
282 try
283 {
284 SSLContext sslContext = new SSLContextBuilder().useSSL().loadTrustMaterial( null,
285 new RelaxedTrustStrategy(
286 IGNORE_SSL_VALIDITY_DATES ) ).build();
287 sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslContext, sslProtocols, cipherSuites,
288 SSL_ALLOW_ALL
289 ? SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
290 : SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER );
291 }
292 catch ( Exception ex )
293 {
294 throw new SSLInitializationException( ex.getMessage(), ex );
295 }
296 }
297 else
298 {
299 sslConnectionSocketFactory =
300 new SSLConnectionSocketFactory( HttpsURLConnection.getDefaultSSLSocketFactory(), sslProtocols,
301 cipherSuites,
302 SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER );
303 }
304
305 Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register( "http",
306 PlainConnectionSocketFactory.INSTANCE ).register(
307 "https", sslConnectionSocketFactory ).build();
308
309 PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( registry );
310 if ( PERSISTENT_POOL )
311 {
312 connManager.setDefaultMaxPerRoute( MAX_CONN_PER_ROUTE );
313 connManager.setMaxTotal( MAX_CONN_TOTAL );
314 }
315 else
316 {
317 connManager.setMaxTotal( 1 );
318 }
319 return connManager;
320 }
321
322 private static CloseableHttpClient CLIENT = createClient();
323
324 private static CloseableHttpClient createClient()
325 {
326 return HttpClientBuilder.create().useSystemProperties().disableConnectionState().setConnectionManager(
327 CONN_MAN ).build();
328 }
329
330 private static String DEFAULT_USER_AGENT = getDefaultUserAgent();
331
332 private static String getDefaultUserAgent()
333 {
334 Properties props = new Properties();
335
336 InputStream is = AbstractHttpClientWagon.class.getResourceAsStream(
337 "/META-INF/maven/org.apache.maven.wagon/wagon-http/pom.properties" );
338 if ( is != null )
339 {
340 try
341 {
342 props.load( is );
343 }
344 catch ( IOException ignore )
345 {
346 }
347 finally
348 {
349 IOUtil.close( is );
350 }
351 }
352
353 String ver = props.getProperty( "version", "unknown-version" );
354 return "Apache-Maven-Wagon/" + ver + " (Java " + System.getProperty( "java.version" ) + "; ";
355 }
356
357
358 private CredentialsProvider credentialsProvider;
359
360 private AuthCache authCache;
361
362 private HttpClientContext localContext;
363
364 private Closeable closeable;
365
366
367
368
369
370 private Properties httpHeaders;
371
372
373
374
375 private HttpConfiguration httpConfiguration;
376
377 public void openConnectionInternal()
378 {
379 repository.setUrl( getURL( repository ) );
380
381 localContext = HttpClientContext.create();
382 credentialsProvider = new BasicCredentialsProvider();
383 authCache = new BasicAuthCache();
384 localContext.setCredentialsProvider( credentialsProvider );
385 localContext.setAuthCache( authCache );
386
387 if ( authenticationInfo != null )
388 {
389
390 String username = authenticationInfo.getUserName();
391 String password = authenticationInfo.getPassword();
392
393 if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) )
394 {
395 Credentials creds = new UsernamePasswordCredentials( username, password );
396
397 String host = getRepository().getHost();
398 int port = getRepository().getPort() > -1 ? getRepository().getPort() : AuthScope.ANY_PORT;
399
400 credentialsProvider.setCredentials( new AuthScope( host, port ), creds );
401 }
402 }
403
404 ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
405 if ( proxyInfo != null )
406 {
407 String proxyUsername = proxyInfo.getUserName();
408 String proxyPassword = proxyInfo.getPassword();
409 String proxyHost = proxyInfo.getHost();
410 String proxyNtlmHost = proxyInfo.getNtlmHost();
411 String proxyNtlmDomain = proxyInfo.getNtlmDomain();
412 if ( proxyHost != null )
413 {
414 if ( proxyUsername != null && proxyPassword != null )
415 {
416 Credentials creds;
417 if ( proxyNtlmHost != null || proxyNtlmDomain != null )
418 {
419 creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
420 }
421 else
422 {
423 creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
424 }
425
426 int port = proxyInfo.getPort() > -1 ? proxyInfo.getPort() : AuthScope.ANY_PORT;
427
428 AuthScope authScope = new AuthScope( proxyHost, port );
429 credentialsProvider.setCredentials( authScope, creds );
430 }
431 }
432 }
433 }
434
435 public void closeConnection()
436 {
437 if ( !PERSISTENT_POOL )
438 {
439 CONN_MAN.closeIdleConnections( 0, TimeUnit.MILLISECONDS );
440 }
441 }
442
443 public void put( File source, String resourceName )
444 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
445 {
446 Resource resource = new Resource( resourceName );
447
448 firePutInitiated( resource, source );
449
450 resource.setContentLength( source.length() );
451
452 resource.setLastModified( source.lastModified() );
453
454 put( null, resource, source );
455 }
456
457 public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
458 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
459 {
460 Resource resource = new Resource( destination );
461
462 firePutInitiated( resource, null );
463
464 resource.setContentLength( contentLength );
465
466 resource.setLastModified( lastModified );
467
468 put( stream, resource, null );
469 }
470
471 private void put( final InputStream stream, Resource resource, File source )
472 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
473 {
474 put( resource, source, new RequestEntityImplementation( stream, resource, this, source ) );
475 }
476
477 private void put( Resource resource, File source, HttpEntity httpEntity )
478 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
479 {
480
481 StringBuilder url = new StringBuilder( getURL( getRepository() ) );
482 String[] parts = StringUtils.split( resource.getName(), "/" );
483 for ( String part : parts )
484 {
485
486
487 if ( !url.toString().endsWith( "/" ) )
488 {
489 url.append( '/' );
490 }
491 url.append( URLEncoder.encode( part ) );
492 }
493 put( resource, source, httpEntity, url.toString() );
494 }
495
496 private void put( Resource resource, File source, HttpEntity httpEntity, String url )
497 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
498 {
499
500
501 try
502 {
503 mkdirs( PathUtils.dirname( resource.getName() ) );
504 }
505 catch ( HttpException he )
506 {
507 fireTransferError( resource, he, TransferEvent.REQUEST_GET );
508 }
509 catch ( IOException e )
510 {
511 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
512 }
513
514
515
516
517 Repository repo = getRepository();
518 HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() );
519 AuthScope targetScope = new AuthScope( targetHost );
520
521 if ( credentialsProvider.getCredentials( targetScope ) != null )
522 {
523 BasicScheme targetAuth = new BasicScheme();
524 try {
525 targetAuth.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "BASIC preemptive"));
526 authCache.put( targetHost, targetAuth );
527 }
528 catch ( MalformedChallengeException ignore )
529 {
530 }
531 }
532
533 HttpPut putMethod = new HttpPut( url );
534
535 firePutStarted( resource, source );
536
537 try
538 {
539 putMethod.setEntity( httpEntity );
540
541 CloseableHttpResponse response = execute( putMethod );
542 try
543 {
544 int statusCode = response.getStatusLine().getStatusCode();
545 String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + ".";
546 fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
547
548
549 switch ( statusCode )
550 {
551
552 case HttpStatus.SC_OK:
553 case HttpStatus.SC_CREATED:
554 case HttpStatus.SC_ACCEPTED:
555 case HttpStatus.SC_NO_CONTENT:
556 break;
557
558 case HttpStatus.SC_MOVED_PERMANENTLY:
559 case HttpStatus.SC_MOVED_TEMPORARILY:
560 case HttpStatus.SC_SEE_OTHER:
561 put( resource, source, httpEntity, calculateRelocatedUrl( response ) );
562 return;
563 case HttpStatus.SC_FORBIDDEN:
564 fireSessionConnectionRefused();
565 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
566
567 case HttpStatus.SC_NOT_FOUND:
568 throw new ResourceDoesNotExistException( "File: " + url + " does not exist" + reasonPhrase );
569
570
571 default:
572 {
573 TransferFailedException e = new TransferFailedException(
574 "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
575 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
576 throw e;
577 }
578 }
579
580 firePutCompleted( resource, source );
581
582 EntityUtils.consume( response.getEntity() );
583 }
584 finally
585 {
586 response.close();
587 }
588 }
589 catch ( IOException e )
590 {
591 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
592
593 throw new TransferFailedException( e.getMessage(), e );
594 }
595 catch ( HttpException e )
596 {
597 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
598
599 throw new TransferFailedException( e.getMessage(), e );
600 }
601 }
602
603 protected String calculateRelocatedUrl( HttpResponse response )
604 {
605 Header locationHeader = response.getFirstHeader( "Location" );
606 String locationField = locationHeader.getValue();
607
608 return locationField.startsWith( "http" ) ? locationField : getURL( getRepository() ) + '/' + locationField;
609 }
610
611 protected void mkdirs( String dirname )
612 throws HttpException, IOException
613 {
614
615 }
616
617 public boolean resourceExists( String resourceName )
618 throws TransferFailedException, AuthorizationException
619 {
620 String repositoryUrl = getRepository().getUrl();
621 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resourceName;
622 HttpHead headMethod = new HttpHead( url );
623 try
624 {
625 CloseableHttpResponse response = execute( headMethod );
626 try
627 {
628 int statusCode = response.getStatusLine().getStatusCode();
629 String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + ".";
630 boolean result;
631 switch ( statusCode )
632 {
633 case HttpStatus.SC_OK:
634 result = true;
635 break;
636 case HttpStatus.SC_NOT_MODIFIED:
637 result = true;
638 break;
639 case HttpStatus.SC_FORBIDDEN:
640 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
641
642 case HttpStatus.SC_UNAUTHORIZED:
643 throw new AuthorizationException( "Not authorized " + reasonPhrase );
644
645 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
646 throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase );
647
648 case HttpStatus.SC_NOT_FOUND:
649 result = false;
650 break;
651
652 default:
653 throw new TransferFailedException(
654 "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
655 }
656
657 EntityUtils.consume( response.getEntity() );
658 return result;
659 }
660 finally
661 {
662 response.close();
663 }
664 }
665 catch ( IOException e )
666 {
667 throw new TransferFailedException( e.getMessage(), e );
668 }
669 catch ( HttpException e )
670 {
671 throw new TransferFailedException( e.getMessage(), e );
672 }
673 }
674
675 protected CloseableHttpResponse execute( HttpUriRequest httpMethod )
676 throws HttpException, IOException
677 {
678 setHeaders( httpMethod );
679 String userAgent = getUserAgent( httpMethod );
680 if ( userAgent != null )
681 {
682 httpMethod.setHeader( HTTP.USER_AGENT, userAgent );
683 }
684
685 RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
686
687 requestConfigBuilder.setCookieSpec( CookieSpecs.BROWSER_COMPATIBILITY );
688
689 Repository repo = getRepository();
690 ProxyInfo proxyInfo = getProxyInfo( repo.getProtocol(), repo.getHost() );
691 if ( proxyInfo != null )
692 {
693 HttpHost proxy = new HttpHost( proxyInfo.getHost(), proxyInfo.getPort() );
694 requestConfigBuilder.setProxy( proxy );
695 }
696
697 HttpMethodConfiguration config =
698 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( httpMethod );
699
700 if ( config != null )
701 {
702 ConfigurationUtils.copyConfig( config, requestConfigBuilder );
703 }
704 else
705 {
706 requestConfigBuilder.setSocketTimeout( getReadTimeout() );
707 }
708
709 localContext.setRequestConfig( requestConfigBuilder.build() );
710
711 if ( config != null && config.isUsePreemptive() )
712 {
713 HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() );
714 AuthScope targetScope = new AuthScope( targetHost );
715
716 if ( credentialsProvider.getCredentials( targetScope ) != null )
717 {
718 BasicScheme targetAuth = new BasicScheme();
719 targetAuth.processChallenge( new BasicHeader(AUTH.WWW_AUTH, "BASIC preemptive" ) );
720 authCache.put( targetHost, targetAuth );
721 }
722 }
723
724 if ( proxyInfo != null )
725 {
726 if ( proxyInfo.getHost() != null )
727 {
728 HttpHost proxyHost = new HttpHost( proxyInfo.getHost(), proxyInfo.getPort() );
729 AuthScope proxyScope = new AuthScope( proxyHost );
730
731 String proxyUsername = proxyInfo.getUserName();
732 String proxyPassword = proxyInfo.getPassword();
733 String proxyNtlmHost = proxyInfo.getNtlmHost();
734 String proxyNtlmDomain = proxyInfo.getNtlmDomain();
735
736 if ( proxyUsername != null && proxyPassword != null )
737 {
738 Credentials creds;
739 if ( proxyNtlmHost != null || proxyNtlmDomain != null )
740 {
741 creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
742 }
743 else
744 {
745 creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
746 }
747
748 credentialsProvider.setCredentials( proxyScope, creds );
749 BasicScheme proxyAuth = new BasicScheme();
750 proxyAuth.processChallenge( new BasicHeader(AUTH.PROXY_AUTH, "BASIC preemptive" ) );
751 authCache.put( proxyHost, proxyAuth );
752 }
753 }
754 }
755
756 return CLIENT.execute( httpMethod, localContext );
757 }
758
759 protected void setHeaders( HttpUriRequest method )
760 {
761 HttpMethodConfiguration config =
762 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
763 if ( config == null || config.isUseDefaultHeaders() )
764 {
765
766 method.addHeader( "Cache-control", "no-cache" );
767 method.addHeader( "Cache-store", "no-store" );
768 method.addHeader( "Pragma", "no-cache" );
769 method.addHeader( "Expires", "0" );
770 method.addHeader( "Accept-Encoding", "gzip" );
771 method.addHeader( "User-Agent", DEFAULT_USER_AGENT );
772 }
773
774 if ( httpHeaders != null )
775 {
776 for ( Map.Entry<Object, Object> entry : httpHeaders.entrySet() )
777 {
778 method.addHeader( (String) entry.getKey(), (String) entry.getValue() );
779 }
780 }
781
782 Header[] headers = config == null ? null : config.asRequestHeaders();
783 if ( headers != null )
784 {
785 for ( Header header : headers )
786 {
787 method.addHeader( header );
788 }
789 }
790 }
791
792 protected String getUserAgent( HttpUriRequest method )
793 {
794 if ( httpHeaders != null )
795 {
796 String value = (String) httpHeaders.get( "User-Agent" );
797 if ( value != null )
798 {
799 return value;
800 }
801 }
802 HttpMethodConfiguration config =
803 httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
804
805 if ( config != null )
806 {
807 return (String) config.getHeaders().get( "User-Agent" );
808 }
809 return null;
810 }
811
812
813
814
815
816
817
818
819 protected String getURL( Repository repository )
820 {
821 return repository.getUrl();
822 }
823
824 public HttpConfiguration getHttpConfiguration()
825 {
826 return httpConfiguration;
827 }
828
829 public void setHttpConfiguration( HttpConfiguration httpConfiguration )
830 {
831 this.httpConfiguration = httpConfiguration;
832 }
833
834 public void fillInputData( InputData inputData )
835 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
836 {
837 Resource resource = inputData.getResource();
838
839 String repositoryUrl = getRepository().getUrl();
840 String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resource.getName();
841 HttpGet getMethod = new HttpGet( url );
842 long timestamp = resource.getLastModified();
843 if ( timestamp > 0 )
844 {
845 SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
846 fmt.setTimeZone( GMT_TIME_ZONE );
847 Header hdr = new BasicHeader( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
848 fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
849 getMethod.addHeader( hdr );
850 }
851
852 try
853 {
854 CloseableHttpResponse response = execute( getMethod );
855 closeable = response;
856 int statusCode = response.getStatusLine().getStatusCode();
857
858 String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + ".";
859
860 fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
861
862 switch ( statusCode )
863 {
864 case HttpStatus.SC_OK:
865 break;
866
867 case HttpStatus.SC_NOT_MODIFIED:
868
869 return;
870 case HttpStatus.SC_FORBIDDEN:
871 fireSessionConnectionRefused();
872 throw new AuthorizationException( "Access denied to: " + url + " " + reasonPhrase );
873
874 case HttpStatus.SC_UNAUTHORIZED:
875 fireSessionConnectionRefused();
876 throw new AuthorizationException( "Not authorized " + reasonPhrase );
877
878 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
879 fireSessionConnectionRefused();
880 throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase );
881
882 case HttpStatus.SC_NOT_FOUND:
883 throw new ResourceDoesNotExistException( "File: " + url + " " + reasonPhrase );
884
885
886 default:
887 {
888 cleanupGetTransfer( resource );
889 TransferFailedException e = new TransferFailedException(
890 "Failed to transfer file: " + url + ". Return code is: " + statusCode + " " + reasonPhrase );
891 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
892 throw e;
893 }
894 }
895
896 Header contentLengthHeader = response.getFirstHeader( "Content-Length" );
897
898 if ( contentLengthHeader != null )
899 {
900 try
901 {
902 long contentLength = Long.parseLong( contentLengthHeader.getValue() );
903
904 resource.setContentLength( contentLength );
905 }
906 catch ( NumberFormatException e )
907 {
908 fireTransferDebug(
909 "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
910 }
911 }
912
913 Header lastModifiedHeader = response.getFirstHeader( "Last-Modified" );
914 if ( lastModifiedHeader != null )
915 {
916 Date lastModified = DateUtils.parseDate( lastModifiedHeader.getValue() );
917 if ( lastModified != null )
918 {
919 resource.setLastModified( lastModified.getTime() );
920 fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() +
921 " (" + lastModified.getTime() + ")" );
922 }
923 }
924
925 HttpEntity entity = response.getEntity();
926 if ( entity != null )
927 {
928 inputData.setInputStream( entity.getContent() );
929 }
930 }
931 catch ( IOException e )
932 {
933 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
934
935 throw new TransferFailedException( e.getMessage(), e );
936 }
937 catch ( HttpException e )
938 {
939 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
940
941 throw new TransferFailedException( e.getMessage(), e );
942 }
943 }
944
945 protected void cleanupGetTransfer( Resource resource )
946 {
947 if ( closeable != null )
948 {
949 try
950 {
951 closeable.close();
952 }
953 catch ( IOException ignore )
954 {
955 }
956
957 }
958 }
959
960
961 @Override
962 public void putFromStream( InputStream stream, String destination )
963 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
964 {
965 putFromStream( stream, destination, -1, -1 );
966 }
967
968 @Override
969 protected void putFromStream( InputStream stream, Resource resource )
970 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
971 {
972 putFromStream( stream, resource.getName(), -1, -1 );
973 }
974
975 public Properties getHttpHeaders()
976 {
977 return httpHeaders;
978 }
979
980 public void setHttpHeaders( Properties httpHeaders )
981 {
982 this.httpHeaders = httpHeaders;
983 }
984
985 @Override
986 public void fillOutputData( OutputData outputData )
987 throws TransferFailedException
988 {
989
990 throw new IllegalStateException( "this wagon http client must not use fillOutputData" );
991 }
992 }