View Javadoc
1   package org.apache.maven.wagon.providers.webdav;
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.commons.httpclient.Credentials;
23  import org.apache.commons.httpclient.Header;
24  import org.apache.commons.httpclient.HostConfiguration;
25  import org.apache.commons.httpclient.HttpClient;
26  import org.apache.commons.httpclient.HttpConnectionManager;
27  import org.apache.commons.httpclient.HttpMethod;
28  import org.apache.commons.httpclient.HttpStatus;
29  import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
30  import org.apache.commons.httpclient.NTCredentials;
31  import org.apache.commons.httpclient.UsernamePasswordCredentials;
32  import org.apache.commons.httpclient.auth.AuthScope;
33  import org.apache.commons.httpclient.cookie.CookiePolicy;
34  import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
35  import org.apache.commons.httpclient.methods.GetMethod;
36  import org.apache.commons.httpclient.methods.HeadMethod;
37  import org.apache.commons.httpclient.methods.PutMethod;
38  import org.apache.commons.httpclient.methods.RequestEntity;
39  import org.apache.commons.httpclient.params.HttpMethodParams;
40  import org.apache.commons.httpclient.util.DateParseException;
41  import org.apache.commons.httpclient.util.DateUtil;
42  import org.apache.commons.io.IOUtils;
43  import org.apache.commons.lang.StringUtils;
44  import org.apache.maven.wagon.InputData;
45  import org.apache.maven.wagon.OutputData;
46  import org.apache.maven.wagon.PathUtils;
47  import org.apache.maven.wagon.ResourceDoesNotExistException;
48  import org.apache.maven.wagon.StreamWagon;
49  import org.apache.maven.wagon.TransferFailedException;
50  import org.apache.maven.wagon.Wagon;
51  import org.apache.maven.wagon.authorization.AuthorizationException;
52  import org.apache.maven.wagon.events.TransferEvent;
53  import org.apache.maven.wagon.proxy.ProxyInfo;
54  import org.apache.maven.wagon.repository.Repository;
55  import org.apache.maven.wagon.resource.Resource;
56  import org.apache.maven.wagon.shared.http.EncodingUtil;
57  import org.codehaus.plexus.util.IOUtil;
58  
59  import java.io.ByteArrayInputStream;
60  import java.io.File;
61  import java.io.FileInputStream;
62  import java.io.IOException;
63  import java.io.InputStream;
64  import java.io.OutputStream;
65  import java.nio.ByteBuffer;
66  import java.text.SimpleDateFormat;
67  import java.util.Date;
68  import java.util.Locale;
69  import java.util.Properties;
70  import java.util.TimeZone;
71  import java.util.zip.GZIPInputStream;
72  
73  /**
74   * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
75   * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
76   */
77  public abstract class AbstractHttpClientWagon
78      extends StreamWagon
79  {
80      private final class RequestEntityImplementation
81          implements RequestEntity
82      {
83          private final Resource resource;
84  
85          private final Wagon wagon;
86  
87          private File source;
88  
89          private ByteBuffer byteBuffer;
90  
91          private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon,
92                                               final File source )
93              throws TransferFailedException
94          {
95              if ( source != null )
96              {
97                  this.source = source;
98              }
99              else
100             {
101                 try
102                 {
103                     byte[] bytes = IOUtils.toByteArray( stream );
104                     this.byteBuffer = ByteBuffer.allocate( bytes.length );
105                     this.byteBuffer.put( bytes );
106                 }
107                 catch ( IOException e )
108                 {
109                     throw new TransferFailedException( e.getMessage(), e );
110                 }
111             }
112 
113             this.resource = resource;
114             this.wagon = wagon;
115         }
116 
117         public long getContentLength()
118         {
119             return resource.getContentLength();
120         }
121 
122         public String getContentType()
123         {
124             return null;
125         }
126 
127         public boolean isRepeatable()
128         {
129             return true;
130         }
131 
132         public void writeRequest( OutputStream output )
133             throws IOException
134         {
135             byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
136 
137             TransferEvent transferEvent =
138                 new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
139             transferEvent.setTimestamp( System.currentTimeMillis() );
140 
141             InputStream fin = null;
142             try
143             {
144                 fin = this.source != null
145                     ? new FileInputStream( source )
146                     : new ByteArrayInputStream( this.byteBuffer.array() );
147                 int remaining = Integer.MAX_VALUE;
148                 while ( remaining > 0 )
149                 {
150                     int n = fin.read( buffer, 0, Math.min( buffer.length, remaining ) );
151 
152                     if ( n == -1 )
153                     {
154                         break;
155                     }
156 
157                     fireTransferProgress( transferEvent, buffer, n );
158 
159                     output.write( buffer, 0, n );
160 
161                     remaining -= n;
162                 }
163             }
164             finally
165             {
166                 IOUtils.closeQuietly( fin );
167             }
168 
169             output.flush();
170         }
171     }
172 
173     protected static final int SC_NULL = -1;
174 
175     protected static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
176 
177     private HttpClient client;
178 
179     protected HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
180 
181     /**
182      * @deprecated Use httpConfiguration instead.
183      */
184     private Properties httpHeaders;
185 
186     /**
187      * @since 1.0-beta-6
188      */
189     private HttpConfiguration httpConfiguration;
190 
191     private HttpMethod getMethod;
192 
193     public void openConnectionInternal()
194     {
195         repository.setUrl( getURL( repository ) );
196         client = new HttpClient( connectionManager );
197 
198         // WAGON-273: default the cookie-policy to browser compatible
199         client.getParams().setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY );
200 
201         String username = null;
202         String password = null;
203         String domain = null;
204 
205         if ( authenticationInfo != null )
206         {
207             username = authenticationInfo.getUserName();
208 
209             if ( StringUtils.contains( username, "\\" ) )
210             {
211                 String[] domainAndUsername = username.split( "\\\\" );
212                 domain = domainAndUsername[0];
213                 username = domainAndUsername[1];
214             }
215 
216             password = authenticationInfo.getPassword();
217 
218 
219         }
220 
221         String host = getRepository().getHost();
222 
223         if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) )
224         {
225             Credentials creds;
226             if ( domain != null )
227             {
228                 creds = new NTCredentials( username, password, host, domain );
229             }
230             else
231             {
232                 creds = new UsernamePasswordCredentials( username, password );
233             }
234 
235             int port = getRepository().getPort() > -1 ? getRepository().getPort() : AuthScope.ANY_PORT;
236 
237             AuthScope scope = new AuthScope( host, port );
238             client.getState().setCredentials( scope, creds );
239         }
240 
241         HostConfiguration hc = new HostConfiguration();
242 
243         ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
244         if ( proxyInfo != null )
245         {
246             String proxyUsername = proxyInfo.getUserName();
247             String proxyPassword = proxyInfo.getPassword();
248             String proxyHost = proxyInfo.getHost();
249             int proxyPort = proxyInfo.getPort();
250             String proxyNtlmHost = proxyInfo.getNtlmHost();
251             String proxyNtlmDomain = proxyInfo.getNtlmDomain();
252             if ( proxyHost != null )
253             {
254                 hc.setProxy( proxyHost, proxyPort );
255 
256                 if ( proxyUsername != null && proxyPassword != null )
257                 {
258                     Credentials creds;
259                     if ( proxyNtlmHost != null || proxyNtlmDomain != null )
260                     {
261                         creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
262                     }
263                     else
264                     {
265                         creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
266                     }
267 
268                     int port = proxyInfo.getPort() > -1 ? proxyInfo.getPort() : AuthScope.ANY_PORT;
269 
270                     AuthScope scope = new AuthScope( proxyHost, port );
271                     client.getState().setProxyCredentials( scope, creds );
272                 }
273             }
274         }
275 
276         hc.setHost( host );
277 
278         //start a session with the webserver
279         client.setHostConfiguration( hc );
280     }
281 
282     public void closeConnection()
283     {
284         if ( connectionManager instanceof MultiThreadedHttpConnectionManager )
285         {
286             ( (MultiThreadedHttpConnectionManager) connectionManager ).shutdown();
287         }
288     }
289 
290     public void put( File source, String resourceName )
291         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
292     {
293         Resource resource = new Resource( resourceName );
294 
295         firePutInitiated( resource, source );
296 
297         resource.setContentLength( source.length() );
298 
299         resource.setLastModified( source.lastModified() );
300 
301         put( null, resource, source );
302     }
303 
304     public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
305         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
306     {
307         Resource resource = new Resource( destination );
308 
309         firePutInitiated( resource, null );
310 
311         resource.setContentLength( contentLength );
312 
313         resource.setLastModified( lastModified );
314 
315         put( stream, resource, null );
316     }
317 
318     private void put( final InputStream stream, Resource resource, File source )
319         throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
320     {
321         RequestEntityImplementation requestEntityImplementation =
322             new RequestEntityImplementation( stream, resource, this, source );
323 
324         put( resource, source, requestEntityImplementation, buildUrl( resource ) );
325 
326     }
327 
328     /**
329      * Builds a complete URL string from the repository URL and the relative path of the resource passed.
330      *
331      * @param resource the resource to extract the relative path from.
332      * @return the complete URL
333      */
334     private String buildUrl( Resource resource )
335     {
336         return EncodingUtil.encodeURLToString( getRepository().getUrl(), resource.getName() );
337     }
338 
339     private void put( Resource resource, File source, RequestEntityImplementation requestEntityImplementation,
340                       String url )
341         throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
342     {
343         // preemptive true for put
344         client.getParams().setAuthenticationPreemptive( true );
345 
346         //Parent directories need to be created before posting
347         try
348         {
349             mkdirs( PathUtils.dirname( resource.getName() ) );
350         }
351         catch ( IOException e )
352         {
353             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
354         }
355 
356         PutMethod putMethod = new PutMethod( url );
357 
358         firePutStarted( resource, source );
359 
360         try
361         {
362             putMethod.setRequestEntity( requestEntityImplementation );
363 
364             int statusCode;
365             try
366             {
367                 statusCode = execute( putMethod );
368 
369             }
370             catch ( IOException e )
371             {
372                 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
373 
374                 throw new TransferFailedException( e.getMessage(), e );
375             }
376 
377             fireTransferDebug( url + " - Status code: " + statusCode );
378 
379             // Check that we didn't run out of retries.
380             // CHECKSTYLE_OFF: AvoidNestedBlocks
381             switch ( statusCode )
382             {
383                 // Success Codes
384                 case HttpStatus.SC_OK: // 200
385                 case HttpStatus.SC_CREATED: // 201
386                 case HttpStatus.SC_ACCEPTED: // 202
387                 case HttpStatus.SC_NO_CONTENT:  // 204
388                     break;
389 
390                 // handle all redirect even if http specs says
391                 // " the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user"
392                 case HttpStatus.SC_MOVED_PERMANENTLY: // 301
393                 case HttpStatus.SC_MOVED_TEMPORARILY: // 302
394                 case HttpStatus.SC_SEE_OTHER: // 303
395                     String relocatedUrl = calculateRelocatedUrl( putMethod );
396                     fireTransferDebug( "relocate to " + relocatedUrl );
397                     put( resource, source, requestEntityImplementation, relocatedUrl );
398                     return;
399 
400                 case SC_NULL:
401                 {
402                     TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
403                     fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
404                     throw e;
405                 }
406 
407                 case HttpStatus.SC_FORBIDDEN:
408                     fireSessionConnectionRefused();
409                     throw new AuthorizationException( "Access denied to: " + url );
410 
411                 case HttpStatus.SC_NOT_FOUND:
412                     throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
413 
414                     //add more entries here
415                 default:
416                 {
417                     TransferFailedException e = new TransferFailedException(
418                         "Failed to transfer file: " + url + ". Return code is: " + statusCode );
419                     fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
420                     throw e;
421                 }
422             }
423             // CHECKSTYLE_ON: AvoidNestedBlocks
424 
425             firePutCompleted( resource, source );
426         }
427         finally
428         {
429             putMethod.releaseConnection();
430         }
431     }
432 
433     protected String calculateRelocatedUrl( EntityEnclosingMethod method )
434     {
435         Header locationHeader = method.getResponseHeader( "Location" );
436         String locationField = locationHeader.getValue();
437         // is it a relative Location or a full ?
438         return locationField.startsWith( "http" ) ? locationField : getURL( getRepository() ) + '/' + locationField;
439     }
440 
441     protected void mkdirs( String dirname )
442         throws IOException
443     {
444         // do nothing as default.
445     }
446 
447     public boolean resourceExists( String resourceName )
448         throws TransferFailedException, AuthorizationException
449     {
450         StringBuilder url = new StringBuilder( getRepository().getUrl() );
451         if ( !url.toString().endsWith( "/" ) )
452         {
453             url.append( '/' );
454         }
455         url.append( resourceName );
456         HeadMethod headMethod = new HeadMethod( url.toString() );
457 
458         int statusCode;
459         try
460         {
461             statusCode = execute( headMethod );
462         }
463         catch ( IOException e )
464         {
465             throw new TransferFailedException( e.getMessage(), e );
466         }
467         try
468         {
469             switch ( statusCode )
470             {
471                 case HttpStatus.SC_OK:
472                     return true;
473 
474                 case HttpStatus.SC_NOT_MODIFIED:
475                     return true;
476 
477                 case SC_NULL:
478                     throw new TransferFailedException( "Failed to transfer file: " + url );
479 
480                 case HttpStatus.SC_FORBIDDEN:
481                     throw new AuthorizationException( "Access denied to: " + url );
482 
483                 case HttpStatus.SC_UNAUTHORIZED:
484                     throw new AuthorizationException( "Not authorized." );
485 
486                 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
487                     throw new AuthorizationException( "Not authorized by proxy." );
488 
489                 case HttpStatus.SC_NOT_FOUND:
490                     return false;
491 
492                 //add more entries here
493                 default:
494                     throw new TransferFailedException(
495                         "Failed to transfer file: " + url + ". Return code is: " + statusCode );
496             }
497         }
498         finally
499         {
500             headMethod.releaseConnection();
501         }
502     }
503 
504     protected int execute( HttpMethod httpMethod )
505         throws IOException
506     {
507         int statusCode;
508 
509         setParameters( httpMethod );
510         setHeaders( httpMethod );
511 
512         statusCode = client.executeMethod( httpMethod );
513         return statusCode;
514     }
515 
516     protected void setParameters( HttpMethod method )
517     {
518         HttpMethodConfiguration config =
519             httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
520         if ( config != null )
521         {
522             HttpMethodParams params = config.asMethodParams( method.getParams() );
523             if ( params != null )
524             {
525                 method.setParams( params );
526             }
527         }
528 
529         if ( config == null || config.getConnectionTimeout() == HttpMethodConfiguration.DEFAULT_CONNECTION_TIMEOUT )
530         {
531             method.getParams().setSoTimeout( getTimeout() );
532         }
533     }
534 
535     protected void setHeaders( HttpMethod method )
536     {
537         HttpMethodConfiguration config =
538             httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
539         if ( config == null || config.isUseDefaultHeaders() )
540         {
541             // TODO: merge with the other headers and have some better defaults, unify with lightweight headers
542             method.addRequestHeader( "Cache-control", "no-cache" );
543             method.addRequestHeader( "Cache-store", "no-store" );
544             method.addRequestHeader( "Pragma", "no-cache" );
545             method.addRequestHeader( "Expires", "0" );
546             method.addRequestHeader( "Accept-Encoding", "gzip" );
547             method.addRequestHeader( "User-Agent", DEFAULT_USER_AGENT );
548         }
549 
550         if ( httpHeaders != null )
551         {
552             for ( Object header : httpHeaders.keySet() )
553             {
554                 if ( "User-Agent".equals( header ) )
555                 {
556                     method.setRequestHeader( (String) header, httpHeaders.getProperty( (String) header ) );
557                 }
558                 else
559                 {
560                     method.addRequestHeader( (String) header, httpHeaders.getProperty( (String) header ) );
561                 }
562             }
563         }
564 
565         Header[] headers = config == null ? null : config.asRequestHeaders();
566         if ( headers != null )
567         {
568             for ( Header header : headers )
569             {
570                 method.addRequestHeader( header );
571             }
572         }
573     }
574     
575     private static final String DEFAULT_USER_AGENT = getDefaultUserAgent();
576 
577     private static String getDefaultUserAgent()
578     {
579         Properties props = new Properties();
580 
581         InputStream is = AbstractHttpClientWagon.class.getResourceAsStream(
582             "/META-INF/maven/org.apache.maven.wagon/wagon-webdav-jackrabbit/pom.properties" );
583         if ( is != null )
584         {
585             try
586             {
587                 props.load( is );
588             }
589             catch ( IOException ignore )
590             {
591                 // ignore
592             }
593             finally
594             {
595                 IOUtil.close( is );
596             }
597         }
598 
599         String ver = props.getProperty( "version", "unknown-version" );
600         return "Apache-Maven-Wagon/" + ver + " (Java " + System.getProperty( "java.version" ) + "; ";
601     }
602 
603     /**
604      * getUrl
605      * Implementors can override this to remove unwanted parts of the url such as role-hints
606      *
607      * @param repository
608      * @return
609      */
610     protected String getURL( Repository repository )
611     {
612         return repository.getUrl();
613     }
614 
615     protected HttpClient getClient()
616     {
617         return client;
618     }
619 
620     public void setConnectionManager( HttpConnectionManager connectionManager )
621     {
622         this.connectionManager = connectionManager;
623     }
624 
625     public Properties getHttpHeaders()
626     {
627         return httpHeaders;
628     }
629 
630     public void setHttpHeaders( Properties httpHeaders )
631     {
632         this.httpHeaders = httpHeaders;
633     }
634 
635     public HttpConfiguration getHttpConfiguration()
636     {
637         return httpConfiguration;
638     }
639 
640     public void setHttpConfiguration( HttpConfiguration httpConfiguration )
641     {
642         this.httpConfiguration = httpConfiguration;
643     }
644 
645     public void fillInputData( InputData inputData )
646         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
647     {
648         Resource resource = inputData.getResource();
649 
650         StringBuilder url = new StringBuilder( getRepository().getUrl() );
651         if ( !url.toString().endsWith( "/" ) )
652         {
653             url.append( '/' );
654         }
655         url.append( resource.getName() );
656 
657         getMethod = new GetMethod( url.toString() );
658 
659         long timestamp = resource.getLastModified();
660         if ( timestamp > 0 )
661         {
662             SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
663             fmt.setTimeZone( GMT_TIME_ZONE );
664             Header hdr = new Header( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
665             fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
666             getMethod.addRequestHeader( hdr );
667         }
668 
669         int statusCode;
670         try
671         {
672             statusCode = execute( getMethod );
673         }
674         catch ( IOException e )
675         {
676             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
677 
678             throw new TransferFailedException( e.getMessage(), e );
679         }
680 
681         fireTransferDebug( url + " - Status code: " + statusCode );
682 
683         // TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is
684         // required
685         // CHECKSTYLE_OFF: AvoidNestedBlocks
686         switch ( statusCode )
687         {
688             case HttpStatus.SC_OK:
689                 break;
690 
691             case HttpStatus.SC_NOT_MODIFIED:
692                 // return, leaving last modified set to original value so getIfNewer should return unmodified
693                 return;
694 
695             case SC_NULL:
696             {
697                 TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
698                 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
699                 throw e;
700             }
701 
702             case HttpStatus.SC_FORBIDDEN:
703                 fireSessionConnectionRefused();
704                 throw new AuthorizationException( "Access denied to: " + url );
705 
706             case HttpStatus.SC_UNAUTHORIZED:
707                 fireSessionConnectionRefused();
708                 throw new AuthorizationException( "Not authorized." );
709 
710             case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
711                 fireSessionConnectionRefused();
712                 throw new AuthorizationException( "Not authorized by proxy." );
713 
714             case HttpStatus.SC_NOT_FOUND:
715                 throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
716 
717                 // add more entries here
718             default:
719             {
720                 cleanupGetTransfer( resource );
721                 TransferFailedException e = new TransferFailedException(
722                     "Failed to transfer file: " + url + ". Return code is: " + statusCode );
723                 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
724                 throw e;
725             }
726         }
727         // CHECKSTYLE_ON: AvoidNestedBlocks
728 
729         InputStream is = null;
730 
731         Header contentLengthHeader = getMethod.getResponseHeader( "Content-Length" );
732 
733         if ( contentLengthHeader != null )
734         {
735             try
736             {
737                 long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
738 
739                 resource.setContentLength( contentLength );
740             }
741             catch ( NumberFormatException e )
742             {
743                 fireTransferDebug(
744                     "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
745             }
746         }
747 
748         Header lastModifiedHeader = getMethod.getResponseHeader( "Last-Modified" );
749 
750         long lastModified = 0;
751 
752         if ( lastModifiedHeader != null )
753         {
754             try
755             {
756                 lastModified = DateUtil.parseDate( lastModifiedHeader.getValue() ).getTime();
757 
758                 resource.setLastModified( lastModified );
759             }
760             catch ( DateParseException e )
761             {
762                 fireTransferDebug( "Unable to parse last modified header" );
763             }
764 
765             fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
766         }
767 
768         Header contentEncoding = getMethod.getResponseHeader( "Content-Encoding" );
769         boolean isGZipped = contentEncoding != null && "gzip".equalsIgnoreCase( contentEncoding.getValue() );
770 
771         try
772         {
773             is = getMethod.getResponseBodyAsStream();
774             if ( isGZipped )
775             {
776                 is = new GZIPInputStream( is );
777             }
778         }
779         catch ( IOException e )
780         {
781             fireTransferError( resource, e, TransferEvent.REQUEST_GET );
782 
783             String msg =
784                 "Error occurred while retrieving from remote repository:" + getRepository() + ": " + e.getMessage();
785 
786             throw new TransferFailedException( msg, e );
787         }
788 
789         inputData.setInputStream( is );
790     }
791 
792     protected void cleanupGetTransfer( Resource resource )
793     {
794         if ( getMethod != null )
795         {
796             getMethod.releaseConnection();
797         }
798     }
799 
800     @Override
801     public void putFromStream( InputStream stream, String destination )
802         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
803     {
804         putFromStream( stream, destination, -1, -1 );
805     }
806 
807     @Override
808     protected void putFromStream( InputStream stream, Resource resource )
809         throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
810     {
811         putFromStream( stream, resource.getName(), -1, -1 );
812     }
813 
814     @Override
815     public void fillOutputData( OutputData outputData )
816         throws TransferFailedException
817     {
818         // no needed in this implementation but throw an Exception if used
819         throw new IllegalStateException( "this wagon http client must not use fillOutputData" );
820     }
821 }