View Javadoc

1   package org.apache.maven.wagon.shared.http;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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.Credentials;
30  import org.apache.http.auth.NTCredentials;
31  import org.apache.http.auth.UsernamePasswordCredentials;
32  import org.apache.http.client.AuthCache;
33  import org.apache.http.client.methods.HttpGet;
34  import org.apache.http.client.methods.HttpHead;
35  import org.apache.http.client.methods.HttpPut;
36  import org.apache.http.client.methods.HttpUriRequest;
37  import org.apache.http.client.params.ClientPNames;
38  import org.apache.http.client.params.CookiePolicy;
39  import org.apache.http.client.protocol.ClientContext;
40  import org.apache.http.conn.ClientConnectionManager;
41  import org.apache.http.conn.params.ConnRoutePNames;
42  import org.apache.http.conn.scheme.Scheme;
43  import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
44  import org.apache.http.conn.ssl.SSLSocketFactory;
45  import org.apache.http.conn.ssl.X509HostnameVerifier;
46  import org.apache.http.impl.auth.BasicScheme;
47  import org.apache.http.impl.client.BasicAuthCache;
48  import org.apache.http.impl.client.DefaultHttpClient;
49  import org.apache.http.impl.conn.SingleClientConnManager;
50  import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
51  import org.apache.http.impl.cookie.DateParseException;
52  import org.apache.http.impl.cookie.DateUtils;
53  import org.apache.http.message.BasicHeader;
54  import org.apache.http.params.CoreConnectionPNames;
55  import org.apache.http.params.CoreProtocolPNames;
56  import org.apache.http.params.HttpParams;
57  import org.apache.http.protocol.BasicHttpContext;
58  import org.apache.maven.wagon.InputData;
59  import org.apache.maven.wagon.OutputData;
60  import org.apache.maven.wagon.PathUtils;
61  import org.apache.maven.wagon.ResourceDoesNotExistException;
62  import org.apache.maven.wagon.StreamWagon;
63  import org.apache.maven.wagon.TransferFailedException;
64  import org.apache.maven.wagon.Wagon;
65  import org.apache.maven.wagon.authorization.AuthorizationException;
66  import org.apache.maven.wagon.events.TransferEvent;
67  import org.apache.maven.wagon.proxy.ProxyInfo;
68  import org.apache.maven.wagon.repository.Repository;
69  import org.apache.maven.wagon.resource.Resource;
70  import org.codehaus.plexus.util.IOUtil;
71  import org.codehaus.plexus.util.StringUtils;
72  
73  import javax.net.ssl.SSLException;
74  import javax.net.ssl.SSLSession;
75  import javax.net.ssl.SSLSocket;
76  import java.io.File;
77  import java.io.FileInputStream;
78  import java.io.FileOutputStream;
79  import java.io.IOException;
80  import java.io.InputStream;
81  import java.io.OutputStream;
82  import java.net.URLEncoder;
83  import java.security.cert.X509Certificate;
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.zip.GZIPInputStream;
91  
92  /**
93   * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
94   * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
95   */
96  public abstract class AbstractHttpClientWagon
97      extends StreamWagon
98  {
99  
100     private BasicHttpContext localContext;
101 
102     private final class RequestEntityImplementation
103         implements HttpEntity
104     {
105         private final Resource resource;
106 
107         private final Wagon wagon;
108 
109         private final File source;
110 
111 
112         private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon,
113                                              final File source )
114             throws TransferFailedException
115         {
116             if ( source != null )
117             {
118                 this.source = source;
119             }
120             else
121             {
122                 FileOutputStream fos = null;
123                 try
124                 {
125                     this.source = File.createTempFile( "http-wagon.", ".tmp" );
126                     this.source.deleteOnExit();
127 
128                     fos = new FileOutputStream( this.source );
129                     IOUtil.copy( stream, fos );
130                 }
131                 catch ( IOException e )
132                 {
133                     fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
134                     throw new TransferFailedException( "Failed to buffer stream contents to temp file for upload.", e );
135                 }
136                 finally
137                 {
138                     IOUtil.close( fos );
139                 }
140             }
141 
142             this.resource = resource;
143             this.wagon = wagon;
144         }
145 
146         public long getContentLength()
147         {
148             return resource.getContentLength();
149         }
150 
151         public Header getContentType()
152         {
153             return null;
154         }
155 
156         public Header getContentEncoding()
157         {
158             return null;
159         }
160 
161         public InputStream getContent()
162             throws IOException, IllegalStateException
163         {
164             FileInputStream fis = new FileInputStream( source );
165 
166             return fis;
167         }
168 
169         public boolean isRepeatable()
170         {
171             return true;
172         }
173 
174         public boolean isChunked()
175         {
176             return false;
177         }
178 
179         public void writeTo( OutputStream output )
180             throws IOException
181         {
182             byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
183 
184             TransferEvent transferEvent =
185                 new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
186             transferEvent.setTimestamp( System.currentTimeMillis() );
187 
188             FileInputStream fin = null;
189             try
190             {
191                 fin = new FileInputStream( source );
192                 int remaining = Integer.MAX_VALUE;
193                 while ( remaining > 0 )
194                 {
195                     int n = fin.read( buffer, 0, Math.min( buffer.length, remaining ) );
196 
197                     if ( n == -1 )
198                     {
199                         break;
200                     }
201 
202                     fireTransferProgress( transferEvent, buffer, n );
203 
204                     output.write( buffer, 0, n );
205 
206                     remaining -= n;
207                 }
208             }
209             finally
210             {
211                 IOUtil.close( fin );
212             }
213 
214             output.flush();
215         }
216 
217         public boolean isStreaming()
218         {
219             return false;
220         }
221 
222         public void consumeContent()
223             throws IOException
224         {
225         }
226     }
227 
228     protected static final int SC_NULL = -1;
229 
230     protected static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
231 
232     private DefaultHttpClient client;
233 
234     /**
235      * @since 2.0
236      */
237     protected static ClientConnectionManager connectionManagerPooled;
238 
239     /**
240      * @since 2.0
241      */
242     protected ClientConnectionManager clientConnectionManager = new SingleClientConnManager();
243 
244     /**
245      * use http(s) connection pool mechanism.
246      * <b>enabled by default</b>
247      *
248      * @since 2.0
249      */
250     protected static boolean useClientManagerPooled =
251         Boolean.valueOf( System.getProperty( "maven.wagon.http.pool", "true" ) );
252 
253     /**
254      * skip failure on certificate validity checks.
255      * <b>enabled by default</b>
256      *
257      * @since 2.0
258      */
259     protected static boolean sslEasy = Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.easy", "true" ) );
260 
261     /**
262      * ssl hostname verifier is allow all by default. Disable this will use a browser compat hostname verifier
263      * <b>enabled by default</b>
264      *
265      * @since 2.0
266      */
267     protected static boolean sslAllowAll =
268         Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.allowall", "true" ) );
269 
270     /**
271      * if using sslEasy certificate date issues will be ignored
272      * <b>enabled by default</b>
273      *
274      * @since 2.0
275      */
276     protected static boolean IGNORE_SSL_VALIDITY_DATES =
277         Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.ignore.validity.dates", "true" ) );
278 
279     static
280     {
281         if ( !useClientManagerPooled )
282         {
283             System.out.println( "http connection pool disabled in wagon http" );
284         }
285         else
286         {
287 
288             ThreadSafeClientConnManager threadSafeClientConnManager = new ThreadSafeClientConnManager();
289             int maxPerRoute =
290                 Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxPerRoute", "20" ) );
291             threadSafeClientConnManager.setDefaultMaxPerRoute( maxPerRoute );
292             int maxTotal = Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxTotal", "40" ) );
293             threadSafeClientConnManager.setDefaultMaxPerRoute( maxPerRoute );
294             threadSafeClientConnManager.setMaxTotal( maxTotal );
295 
296             if ( sslEasy )
297             {
298                 try
299                 {
300                     SSLSocketFactory sslSocketFactory =
301                         new SSLSocketFactory( EasyX509TrustManager.createEasySSLContext(), sslAllowAll
302                             ? new EasyHostNameVerifier()
303                             : new BrowserCompatHostnameVerifier() );
304                     Scheme httpsScheme = new Scheme( "https", 443, sslSocketFactory );
305 
306                     threadSafeClientConnManager.getSchemeRegistry().register( httpsScheme );
307                 }
308                 catch ( IOException e )
309                 {
310                     throw new RuntimeException( "failed to init SSLSocket Factory " + e.getMessage(), e );
311                 }
312             }
313             System.out.println( " wagon http use multi threaded http connection manager maxPerRoute "
314                                     + threadSafeClientConnManager.getDefaultMaxPerRoute() + ", max total "
315                                     + threadSafeClientConnManager.getMaxTotal() );
316 
317             connectionManagerPooled = threadSafeClientConnManager;
318         }
319     }
320 
321     /**
322      * disable all host name verification
323      *
324      * @since 2.0
325      */
326     private static class EasyHostNameVerifier
327         implements X509HostnameVerifier
328     {
329         public void verify( String s, SSLSocket sslSocket )
330             throws IOException
331         {
332             //no op
333         }
334 
335         public void verify( String s, X509Certificate x509Certificate )
336             throws SSLException
337         {
338             //no op
339         }
340 
341         public void verify( String s, String[] strings, String[] strings1 )
342             throws SSLException
343         {
344             //no op
345         }
346 
347         public boolean verify( String s, SSLSession sslSession )
348         {
349             return true;
350         }
351     }
352 
353     public ClientConnectionManager getConnectionManager()
354     {
355         if ( !useClientManagerPooled )
356         {
357             return clientConnectionManager;
358         }
359         return connectionManagerPooled;
360     }
361 
362     public static void setConnectionManagerPooled( ClientConnectionManager clientConnectionManager )
363     {
364         connectionManagerPooled = clientConnectionManager;
365     }
366 
367     public static void setUseClientManagerPooled( boolean pooledClientManager )
368     {
369         useClientManagerPooled = pooledClientManager;
370     }
371 
372     /**
373      * @plexus.configuration
374      * @deprecated Use httpConfiguration instead.
375      */
376     private Properties httpHeaders;
377 
378     /**
379      * @since 1.0-beta-6
380      */
381     private HttpConfiguration httpConfiguration;
382 
383     private HttpGet getMethod;
384 
385     public void openConnectionInternal()
386     {
387         repository.setUrl( getURL( repository ) );
388         client = new DefaultHttpClient( getConnectionManager() );
389 
390         // WAGON-273: default the cookie-policy to browser compatible
391         client.getParams().setParameter( ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY );
392 
393         String username = null;
394         String password = null;
395 
396         if ( authenticationInfo != null )
397         {
398             username = authenticationInfo.getUserName();
399 
400             password = authenticationInfo.getPassword();
401         }
402 
403         if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) )
404         {
405             Credentials creds = new UsernamePasswordCredentials( username, password );
406 
407             String host = getRepository().getHost();
408             int port = getRepository().getPort() > -1 ? getRepository().getPort() : AuthScope.ANY_PORT;
409 
410             client.getCredentialsProvider().setCredentials( new AuthScope( host, port ), creds );
411 
412             AuthCache authCache = new BasicAuthCache();
413             BasicScheme basicAuth = new BasicScheme();
414             HttpHost targetHost = new HttpHost( repository.getHost(), repository.getPort(), repository.getProtocol() );
415             authCache.put( targetHost, basicAuth );
416 
417             localContext = new BasicHttpContext();
418             localContext.setAttribute( ClientContext.AUTH_CACHE, authCache );
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             int proxyPort = proxyInfo.getPort();
428             String proxyNtlmHost = proxyInfo.getNtlmHost();
429             String proxyNtlmDomain = proxyInfo.getNtlmDomain();
430             if ( proxyHost != null )
431             {
432                 HttpHost proxy = new HttpHost( proxyHost, proxyPort );
433 
434                 if ( proxyUsername != null && proxyPassword != null )
435                 {
436                     Credentials creds;
437                     if ( proxyNtlmHost != null || proxyNtlmDomain != null )
438                     {
439                         creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
440                     }
441                     else
442                     {
443                         creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
444                     }
445 
446                     int port = proxyInfo.getPort() > -1 ? proxyInfo.getPort() : AuthScope.ANY_PORT;
447 
448                     AuthScope authScope = new AuthScope( proxyHost, port );
449                     client.getCredentialsProvider().setCredentials( authScope, creds );
450                 }
451 
452                 client.getParams().setParameter( ConnRoutePNames.DEFAULT_PROXY, proxy );
453             }
454         }
455     }
456 
457     public void closeConnection()
458     {
459         if ( !useClientManagerPooled )
460         {
461             getConnectionManager().shutdown();
462         }
463     }
464 
465     public void put( File source, String resourceName )
466         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
467     {
468         Resource resource = new Resource( resourceName );
469 
470         firePutInitiated( resource, source );
471 
472         resource.setContentLength( source.length() );
473 
474         resource.setLastModified( source.lastModified() );
475 
476         put( null, resource, source );
477     }
478 
479     public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
480         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
481     {
482         Resource resource = new Resource( destination );
483 
484         firePutInitiated( resource, null );
485 
486         resource.setContentLength( contentLength );
487 
488         resource.setLastModified( lastModified );
489 
490         put( stream, resource, null );
491     }
492 
493     private void put( final InputStream stream, Resource resource, File source )
494         throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
495     {
496         StringBuilder url = new StringBuilder( getRepository().getUrl() );
497         String[] parts = StringUtils.split( resource.getName(), "/" );
498         for ( String part : parts )
499         {
500             // TODO: Fix encoding...
501             // url += "/" + URLEncoder.encode( parts[i], System.getProperty("file.encoding") );
502             if ( !url.toString().endsWith( "/" ) )
503             {
504                 url.append( '/' );
505             }
506             url.append( URLEncoder.encode( part ) );
507         }
508 
509         //Parent directories need to be created before posting
510         try
511         {
512             mkdirs( PathUtils.dirname( resource.getName() ) );
513         }
514         catch ( HttpException he )
515         {
516             fireTransferError( resource, he, TransferEvent.REQUEST_GET );
517         }
518         catch ( IOException e )
519         {
520             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
521         }
522 
523         HttpPut putMethod = new HttpPut( url.toString() );
524 
525         firePutStarted( resource, source );
526 
527         try
528         {
529             putMethod.setEntity( new RequestEntityImplementation( stream, resource, this, source ) );
530 
531             HttpResponse response;
532             try
533             {
534                 response = execute( putMethod );
535             }
536             catch ( IOException e )
537             {
538                 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
539 
540                 throw new TransferFailedException( e.getMessage(), e );
541             }
542             catch ( HttpException e )
543             {
544                 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
545 
546                 throw new TransferFailedException( e.getMessage(), e );
547             }
548 
549             int statusCode = response.getStatusLine().getStatusCode();
550             String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + ".";
551             fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
552 
553             // Check that we didn't run out of retries.
554             switch ( statusCode )
555             {
556                 // Success Codes
557                 case HttpStatus.SC_OK: // 200
558                 case HttpStatus.SC_CREATED: // 201
559                 case HttpStatus.SC_ACCEPTED: // 202
560                 case HttpStatus.SC_NO_CONTENT:  // 204
561                     break;
562 
563                 case SC_NULL:
564                 {
565                     TransferFailedException e =
566                         new TransferFailedException( "Failed to transfer file: " + url + reasonPhrase );
567                     fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
568                     throw e;
569                 }
570 
571                 case HttpStatus.SC_FORBIDDEN:
572                     fireSessionConnectionRefused();
573                     throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
574 
575                 case HttpStatus.SC_NOT_FOUND:
576                     throw new ResourceDoesNotExistException( "File: " + url + " does not exist" + reasonPhrase );
577 
578                     //add more entries here
579                 default:
580                 {
581                     TransferFailedException e = new TransferFailedException(
582                         "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
583                     fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
584                     throw e;
585                 }
586             }
587 
588             firePutCompleted( resource, source );
589         }
590         finally
591         {
592             putMethod.abort();
593         }
594     }
595 
596     protected void mkdirs( String dirname )
597         throws HttpException, IOException
598     {
599         // nothing to do
600     }
601 
602     public boolean resourceExists( String resourceName )
603         throws TransferFailedException, AuthorizationException
604     {
605         String repositoryUrl = getRepository().getUrl();
606         String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resourceName;
607         HttpHead headMethod = new HttpHead( url );
608         HttpResponse response = null;
609         int statusCode;
610         try
611         {
612             response = execute( headMethod );
613         }
614         catch ( IOException e )
615         {
616             throw new TransferFailedException( e.getMessage(), e );
617         }
618         catch ( HttpException e )
619         {
620             throw new TransferFailedException( e.getMessage(), e );
621         }
622 
623         try
624         {
625             statusCode = response.getStatusLine().getStatusCode();
626             String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + ".";
627             switch ( statusCode )
628             {
629                 case HttpStatus.SC_OK:
630                     return true;
631 
632                 case HttpStatus.SC_NOT_MODIFIED:
633                     return true;
634 
635                 case SC_NULL:
636                     throw new TransferFailedException( "Failed to transfer file: " + url + reasonPhrase );
637 
638                 case HttpStatus.SC_FORBIDDEN:
639                     throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
640 
641                 case HttpStatus.SC_UNAUTHORIZED:
642                     throw new AuthorizationException( "Not authorized" + reasonPhrase );
643 
644                 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
645                     throw new AuthorizationException( "Not authorized by proxy" + reasonPhrase );
646 
647                 case HttpStatus.SC_NOT_FOUND:
648                     return false;
649 
650                 //add more entries here
651                 default:
652                     throw new TransferFailedException(
653                         "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
654             }
655         }
656         finally
657         {
658             headMethod.abort();
659         }
660     }
661 
662     protected HttpResponse execute( HttpUriRequest httpMethod )
663         throws HttpException, IOException
664     {
665         int statusCode = SC_NULL;
666 
667         setParameters( httpMethod );
668         setHeaders( httpMethod );
669         client.getParams().setParameter( CoreProtocolPNames.USER_AGENT, getUserAgent( httpMethod ) );
670 
671         return client.execute( httpMethod, localContext );
672     }
673 
674     protected void setParameters( HttpUriRequest method )
675     {
676         HttpMethodConfiguration config =
677             httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
678         if ( config != null )
679         {
680             HttpParams params = config.asMethodParams( method.getParams() );
681             if ( params != null )
682             {
683                 method.setParams( params );
684             }
685         }
686 
687         if ( config == null || config.getReadTimeout() == HttpMethodConfiguration.DEFAULT_CONNECTION_TIMEOUT )
688         {
689             method.getParams().setParameter( CoreConnectionPNames.SO_TIMEOUT, getTimeout() );
690         }
691     }
692 
693     protected void setHeaders( HttpUriRequest method )
694     {
695         HttpMethodConfiguration config =
696             httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
697         if ( config == null || config.isUseDefaultHeaders() )
698         {
699             // TODO: merge with the other headers and have some better defaults, unify with lightweight headers
700             method.addHeader( "Cache-control", "no-cache" );
701             method.addHeader( "Cache-store", "no-store" );
702             method.addHeader( "Pragma", "no-cache" );
703             method.addHeader( "Expires", "0" );
704             method.addHeader( "Accept-Encoding", "gzip" );
705         }
706 
707         if ( httpHeaders != null )
708         {
709             for ( Map.Entry<Object, Object> entry : httpHeaders.entrySet() )
710             {
711                 method.addHeader( (String) entry.getKey(), (String) entry.getValue() );
712             }
713         }
714 
715         Header[] headers = config == null ? null : config.asRequestHeaders();
716         if ( headers != null )
717         {
718             for ( int i = 0; i < headers.length; i++ )
719             {
720                 method.addHeader( headers[i] );
721             }
722         }
723     }
724 
725     protected String getUserAgent( HttpUriRequest method )
726     {
727         if ( httpHeaders != null )
728         {
729             String value = (String) httpHeaders.get( "User-Agent" );
730             if ( value != null )
731             {
732                 return value;
733             }
734         }
735         HttpMethodConfiguration config =
736             httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
737 
738         if ( config != null )
739         {
740             return (String) config.getHeaders().get( "User-Agent" );
741         }
742         return null;
743     }
744 
745     /**
746      * getUrl
747      * Implementors can override this to remove unwanted parts of the url such as role-hints
748      *
749      * @param repository
750      * @return
751      */
752     protected String getURL( Repository repository )
753     {
754         return repository.getUrl();
755     }
756 
757     public HttpConfiguration getHttpConfiguration()
758     {
759         return httpConfiguration;
760     }
761 
762     public void setHttpConfiguration( HttpConfiguration httpConfiguration )
763     {
764         this.httpConfiguration = httpConfiguration;
765     }
766 
767     public void fillInputData( InputData inputData )
768         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
769     {
770         Resource resource = inputData.getResource();
771 
772         String repositoryUrl = getRepository().getUrl();
773         String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resource.getName();
774         getMethod = new HttpGet( url );
775         long timestamp = resource.getLastModified();
776         if ( timestamp > 0 )
777         {
778             SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
779             fmt.setTimeZone( GMT_TIME_ZONE );
780             Header hdr = new BasicHeader( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
781             fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
782             getMethod.addHeader( hdr );
783         }
784 
785         HttpResponse response;
786         int statusCode;
787         try
788         {
789             response = execute( getMethod );
790         }
791         catch ( IOException e )
792         {
793             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
794 
795             throw new TransferFailedException( e.getMessage(), e );
796         }
797         catch ( HttpException e )
798         {
799             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
800 
801             throw new TransferFailedException( e.getMessage(), e );
802         }
803 
804         statusCode = response.getStatusLine().getStatusCode();
805 
806         String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + ".";
807 
808         fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
809 
810         // TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is
811         // required
812         switch ( statusCode )
813         {
814             case HttpStatus.SC_OK:
815                 break;
816 
817             case HttpStatus.SC_NOT_MODIFIED:
818                 // return, leaving last modified set to original value so getIfNewer should return unmodified
819                 return;
820 
821             case SC_NULL:
822             {
823                 TransferFailedException e =
824                     new TransferFailedException( "Failed to transfer file: " + url + reasonPhrase );
825                 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
826                 throw e;
827             }
828 
829             case HttpStatus.SC_FORBIDDEN:
830                 fireSessionConnectionRefused();
831                 throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
832 
833             case HttpStatus.SC_UNAUTHORIZED:
834                 fireSessionConnectionRefused();
835                 throw new AuthorizationException( "Not authorized" + reasonPhrase );
836 
837             case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
838                 fireSessionConnectionRefused();
839                 throw new AuthorizationException( "Not authorized by proxy" + reasonPhrase );
840 
841             case HttpStatus.SC_NOT_FOUND:
842                 throw new ResourceDoesNotExistException( "File: " + url + reasonPhrase );
843 
844                 // add more entries here
845             default:
846             {
847                 cleanupGetTransfer( resource );
848                 TransferFailedException e = new TransferFailedException(
849                     "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
850                 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
851                 throw e;
852             }
853         }
854 
855         InputStream is;
856 
857         Header contentLengthHeader = response.getFirstHeader( "Content-Length" );
858 
859         if ( contentLengthHeader != null )
860         {
861             try
862             {
863                 long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
864 
865                 resource.setContentLength( contentLength );
866             }
867             catch ( NumberFormatException e )
868             {
869                 fireTransferDebug(
870                     "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
871             }
872         }
873 
874         Header lastModifiedHeader = response.getFirstHeader( "Last-Modified" );
875 
876         long lastModified = 0;
877 
878         if ( lastModifiedHeader != null )
879         {
880             try
881             {
882                 lastModified = DateUtils.parseDate( lastModifiedHeader.getValue() ).getTime();
883 
884                 resource.setLastModified( lastModified );
885             }
886             catch ( DateParseException e )
887             {
888                 fireTransferDebug( "Unable to parse last modified header" );
889             }
890 
891             fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
892         }
893 
894         Header contentEncoding = response.getFirstHeader( "Content-Encoding" );
895         boolean isGZipped = contentEncoding == null ? false : "gzip".equalsIgnoreCase( contentEncoding.getValue() );
896 
897         try
898         {
899             is = response.getEntity().getContent();
900 
901             if ( isGZipped )
902             {
903                 is = new GZIPInputStream( is );
904             }
905         }
906         catch ( IOException e )
907         {
908             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
909 
910             String msg =
911                 "Error occurred while retrieving from remote repository:" + getRepository() + ": " + e.getMessage();
912 
913             throw new TransferFailedException( msg, e );
914         }
915 
916         inputData.setInputStream( is );
917     }
918 
919     protected void cleanupGetTransfer( Resource resource )
920     {
921         if ( getMethod != null )
922         {
923             getMethod.abort();
924         }
925     }
926 
927     public void fillOutputData( OutputData outputData )
928         throws TransferFailedException
929     {
930         throw new IllegalStateException( "Should not be using the streaming wagon for HTTP PUT" );
931     }
932 
933     public Properties getHttpHeaders()
934     {
935         return httpHeaders;
936     }
937 
938     public void setHttpHeaders( Properties httpHeaders )
939     {
940         this.httpHeaders = httpHeaders;
941     }
942 }