1 package org.apache.maven.wagon.shared.http;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.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.HttpException;
28 import org.apache.commons.httpclient.HttpMethod;
29 import org.apache.commons.httpclient.HttpStatus;
30 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
31 import org.apache.commons.httpclient.NTCredentials;
32 import org.apache.commons.httpclient.UsernamePasswordCredentials;
33 import org.apache.commons.httpclient.auth.AuthScope;
34 import org.apache.commons.httpclient.methods.GetMethod;
35 import org.apache.commons.httpclient.methods.HeadMethod;
36 import org.apache.commons.httpclient.methods.PutMethod;
37 import org.apache.commons.httpclient.methods.RequestEntity;
38 import org.apache.commons.httpclient.params.HttpMethodParams;
39 import org.apache.commons.httpclient.util.DateParseException;
40 import org.apache.commons.httpclient.util.DateUtil;
41 import org.apache.maven.wagon.InputData;
42 import org.apache.maven.wagon.OutputData;
43 import org.apache.maven.wagon.PathUtils;
44 import org.apache.maven.wagon.ResourceDoesNotExistException;
45 import org.apache.maven.wagon.StreamWagon;
46 import org.apache.maven.wagon.TransferFailedException;
47 import org.apache.maven.wagon.Wagon;
48 import org.apache.maven.wagon.authorization.AuthorizationException;
49 import org.apache.maven.wagon.events.TransferEvent;
50 import org.apache.maven.wagon.proxy.ProxyInfo;
51 import org.apache.maven.wagon.repository.Repository;
52 import org.apache.maven.wagon.resource.Resource;
53 import org.codehaus.plexus.util.IOUtil;
54 import org.codehaus.plexus.util.StringUtils;
55
56 import java.io.File;
57 import java.io.FileInputStream;
58 import java.io.FileOutputStream;
59 import java.io.IOException;
60 import java.io.InputStream;
61 import java.io.OutputStream;
62 import java.net.URLEncoder;
63 import java.text.SimpleDateFormat;
64 import java.util.Date;
65 import java.util.Iterator;
66 import java.util.Locale;
67 import java.util.Properties;
68 import java.util.TimeZone;
69 import java.util.zip.GZIPInputStream;
70
71
72
73
74
75 public abstract class AbstractHttpClientWagon
76 extends StreamWagon
77 {
78 private final class RequestEntityImplementation
79 implements RequestEntity
80 {
81 private final Resource resource;
82
83 private final Wagon wagon;
84
85 private final File source;
86
87 private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon, final File source )
88 throws TransferFailedException
89 {
90 if ( source != null )
91 {
92 this.source = source;
93 }
94 else
95 {
96 FileOutputStream fos = null;
97 try
98 {
99 this.source = File.createTempFile( "http-wagon.", ".tmp" );
100 this.source.deleteOnExit();
101
102 fos = new FileOutputStream( this.source );
103 IOUtil.copy( stream, fos );
104 }
105 catch ( IOException e )
106 {
107 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
108 throw new TransferFailedException( "Failed to buffer stream contents to temp file for upload.", e );
109 }
110 finally
111 {
112 IOUtil.close( fos );
113 }
114 }
115
116 this.resource = resource;
117 this.wagon = wagon;
118 }
119
120 public long getContentLength()
121 {
122 return resource.getContentLength();
123 }
124
125 public String getContentType()
126 {
127 return null;
128 }
129
130 public boolean isRepeatable()
131 {
132 return true;
133 }
134
135 public void writeRequest( OutputStream output )
136 throws IOException
137 {
138 byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
139
140 TransferEvent transferEvent =
141 new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
142 transferEvent.setTimestamp( System.currentTimeMillis() );
143
144 FileInputStream fin = null;
145 try
146 {
147 fin = new FileInputStream( source );
148 int remaining = Integer.MAX_VALUE;
149 while ( remaining > 0 )
150 {
151 int n = fin.read( buffer, 0, Math.min( buffer.length, remaining ) );
152
153 if ( n == -1 )
154 {
155 break;
156 }
157
158 fireTransferProgress( transferEvent, buffer, n );
159
160 output.write( buffer, 0, n );
161
162 remaining -= n;
163 }
164 }
165 finally
166 {
167 IOUtil.close( fin );
168 }
169
170 output.flush();
171 }
172 }
173
174 protected static final int SC_NULL = -1;
175
176 protected static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
177
178 private HttpClient client;
179
180 protected HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
181
182
183
184
185 private Properties httpHeaders;
186
187
188
189
190 private HttpConfiguration httpConfiguration;
191
192 private HttpMethod getMethod;
193
194 public void openConnectionInternal()
195 {
196 repository.setUrl( getURL( repository ) );
197 client = new HttpClient( connectionManager );
198 String username = null;
199 String password = null;
200
201 if ( authenticationInfo != null )
202 {
203 username = authenticationInfo.getUserName();
204
205 password = authenticationInfo.getPassword();
206 }
207
208 String host = getRepository().getHost();
209
210 if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) )
211 {
212 Credentials creds = new UsernamePasswordCredentials( username, password );
213
214 int port = getRepository().getPort() > -1 ? getRepository().getPort() : AuthScope.ANY_PORT;
215
216 AuthScope scope = new AuthScope( host, port );
217 client.getState().setCredentials( scope, creds );
218 }
219
220 HostConfiguration hc = new HostConfiguration();
221
222 ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
223 if ( proxyInfo != null )
224 {
225 String proxyUsername = proxyInfo.getUserName();
226 String proxyPassword = proxyInfo.getPassword();
227 String proxyHost = proxyInfo.getHost();
228 int proxyPort = proxyInfo.getPort();
229 String proxyNtlmHost = proxyInfo.getNtlmHost();
230 String proxyNtlmDomain = proxyInfo.getNtlmDomain();
231 if ( proxyHost != null )
232 {
233 hc.setProxy( proxyHost, proxyPort );
234
235 if ( proxyUsername != null && proxyPassword != null )
236 {
237 Credentials creds;
238 if ( proxyNtlmHost != null || proxyNtlmDomain != null )
239 {
240 creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
241 }
242 else
243 {
244 creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
245 }
246
247 int port = proxyInfo.getPort() > -1 ? proxyInfo.getPort() : AuthScope.ANY_PORT;
248
249 AuthScope scope = new AuthScope( proxyHost, port );
250 client.getState().setProxyCredentials( scope, creds );
251 }
252 }
253 }
254
255 hc.setHost( host );
256
257
258 client.setHostConfiguration( hc );
259 }
260
261 public void closeConnection()
262 {
263 if ( connectionManager instanceof MultiThreadedHttpConnectionManager )
264 {
265 ( (MultiThreadedHttpConnectionManager) connectionManager ).shutdown();
266 }
267 }
268
269 public void put( File source, String resourceName )
270 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
271 {
272 Resource resource = new Resource( resourceName );
273
274 firePutInitiated( resource, source );
275
276 resource.setContentLength( source.length() );
277
278 resource.setLastModified( source.lastModified() );
279
280 put( null, resource, source );
281 }
282
283 public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
284 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
285 {
286 Resource resource = new Resource( destination );
287
288 firePutInitiated( resource, null );
289
290 resource.setContentLength( contentLength );
291
292 resource.setLastModified( lastModified );
293
294 put( stream, resource, null );
295 }
296
297 private void put( final InputStream stream, Resource resource, File source )
298 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
299 {
300 String url = getRepository().getUrl();
301 String[] parts = StringUtils.split( resource.getName(), "/" );
302 for ( int i = 0; i < parts.length; i++ )
303 {
304
305
306 url += "/" + URLEncoder.encode( parts[i] );
307 }
308
309
310 try
311 {
312 mkdirs( PathUtils.dirname( resource.getName() ) );
313 }
314 catch ( IOException e )
315 {
316 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
317 }
318
319 PutMethod putMethod = new PutMethod( url );
320
321 firePutStarted( resource, source );
322
323 try
324 {
325 putMethod.setRequestEntity( new RequestEntityImplementation( stream, resource, this, source ) );
326
327 int statusCode;
328 try
329 {
330 statusCode = execute( putMethod );
331 }
332 catch ( IOException e )
333 {
334 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
335
336 throw new TransferFailedException( e.getMessage(), e );
337 }
338
339 fireTransferDebug( url + " - Status code: " + statusCode );
340
341
342 switch ( statusCode )
343 {
344
345 case HttpStatus.SC_OK:
346 case HttpStatus.SC_CREATED:
347 case HttpStatus.SC_ACCEPTED:
348 case HttpStatus.SC_NO_CONTENT:
349 break;
350
351 case SC_NULL:
352 {
353 TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
354 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
355 throw e;
356 }
357
358 case HttpStatus.SC_FORBIDDEN:
359 fireSessionConnectionRefused();
360 throw new AuthorizationException( "Access denied to: " + url );
361
362 case HttpStatus.SC_NOT_FOUND:
363 throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
364
365
366 default :
367 {
368 TransferFailedException e =
369 new TransferFailedException( "Failed to transfer file: " + url + ". Return code is: "
370 + statusCode );
371 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
372 throw e;
373 }
374 }
375
376 firePutCompleted( resource, source );
377 }
378 finally
379 {
380 putMethod.releaseConnection();
381 }
382 }
383
384 protected void mkdirs( String dirname ) throws HttpException, IOException
385 {
386 }
387
388 public boolean resourceExists( String resourceName )
389 throws TransferFailedException, AuthorizationException
390 {
391 String url = getRepository().getUrl() + "/" + resourceName;
392 HeadMethod headMethod = new HeadMethod( url );
393 int statusCode;
394 try
395 {
396 statusCode = execute( headMethod );
397 }
398 catch ( IOException e )
399 {
400 throw new TransferFailedException( e.getMessage(), e );
401 }
402 try
403 {
404 switch ( statusCode )
405 {
406 case HttpStatus.SC_OK:
407 return true;
408
409 case HttpStatus.SC_NOT_MODIFIED:
410 return true;
411
412 case SC_NULL:
413 throw new TransferFailedException( "Failed to transfer file: " + url );
414
415 case HttpStatus.SC_FORBIDDEN:
416 throw new AuthorizationException( "Access denied to: " + url );
417
418 case HttpStatus.SC_UNAUTHORIZED:
419 throw new AuthorizationException( "Not authorized." );
420
421 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
422 throw new AuthorizationException( "Not authorized by proxy." );
423
424 case HttpStatus.SC_NOT_FOUND:
425 return false;
426
427
428 default:
429 throw new TransferFailedException( "Failed to transfer file: " + url + ". Return code is: "
430 + statusCode );
431 }
432 }
433 finally
434 {
435 headMethod.releaseConnection();
436 }
437 }
438
439 protected int execute( HttpMethod httpMethod ) throws HttpException, IOException
440 {
441 int statusCode = SC_NULL;
442
443 setParameters( httpMethod );
444 setHeaders( httpMethod );
445
446 statusCode = client.executeMethod( httpMethod );
447 return statusCode;
448 }
449
450 protected void setParameters( HttpMethod method )
451 {
452 HttpMethodConfiguration config = httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
453 if ( config != null )
454 {
455 HttpMethodParams params = config.asMethodParams( method.getParams() );
456 if ( params != null )
457 {
458 method.setParams( params );
459 }
460 }
461
462 if ( config == null || config.getConnectionTimeout() == HttpMethodConfiguration.DEFAULT_CONNECTION_TIMEOUT )
463 {
464 method.getParams().setSoTimeout( getTimeout() );
465 }
466 }
467
468 protected void setHeaders( HttpMethod method )
469 {
470 HttpMethodConfiguration config = httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
471 if ( config == null || config.isUseDefaultHeaders() )
472 {
473
474 method.addRequestHeader( "Cache-control", "no-cache" );
475 method.addRequestHeader( "Cache-store", "no-store" );
476 method.addRequestHeader( "Pragma", "no-cache" );
477 method.addRequestHeader( "Expires", "0" );
478 method.addRequestHeader( "Accept-Encoding", "gzip" );
479 }
480
481 if ( httpHeaders != null )
482 {
483 for ( Iterator i = httpHeaders.keySet().iterator(); i.hasNext(); )
484 {
485 String header = (String) i.next();
486 method.addRequestHeader( header, httpHeaders.getProperty( header ) );
487 }
488 }
489
490 Header[] headers = config == null ? null : config.asRequestHeaders();
491 if ( headers != null )
492 {
493 for ( int i = 0; i < headers.length; i++ )
494 {
495 method.addRequestHeader( headers[i] );
496 }
497 }
498 }
499
500
501
502
503
504
505
506 protected String getURL( Repository repository )
507 {
508 return repository.getUrl();
509 }
510
511 protected HttpClient getClient()
512 {
513 return client;
514 }
515
516 public void setConnectionManager( HttpConnectionManager connectionManager )
517 {
518 this.connectionManager = connectionManager;
519 }
520
521 public Properties getHttpHeaders()
522 {
523 return httpHeaders;
524 }
525
526 public void setHttpHeaders( Properties httpHeaders )
527 {
528 this.httpHeaders = httpHeaders;
529 }
530
531 public HttpConfiguration getHttpConfiguration()
532 {
533 return httpConfiguration;
534 }
535
536 public void setHttpConfiguration( HttpConfiguration httpConfiguration )
537 {
538 this.httpConfiguration = httpConfiguration;
539 }
540
541 public void fillInputData( InputData inputData )
542 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
543 {
544 Resource resource = inputData.getResource();
545
546 String url = getRepository().getUrl() + "/" + resource.getName();
547 getMethod = new GetMethod( url );
548 long timestamp = resource.getLastModified();
549 if ( timestamp > 0 )
550 {
551 SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
552 fmt.setTimeZone( GMT_TIME_ZONE );
553 Header hdr = new Header( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
554 fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
555 getMethod.addRequestHeader( hdr );
556 }
557
558 int statusCode;
559 try
560 {
561 statusCode = execute( getMethod );
562 }
563 catch ( IOException e )
564 {
565 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
566
567 throw new TransferFailedException( e.getMessage(), e );
568 }
569
570 fireTransferDebug( url + " - Status code: " + statusCode );
571
572
573
574 switch ( statusCode )
575 {
576 case HttpStatus.SC_OK:
577 break;
578
579 case HttpStatus.SC_NOT_MODIFIED:
580
581 return;
582
583 case SC_NULL:
584 {
585 TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
586 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
587 throw e;
588 }
589
590 case HttpStatus.SC_FORBIDDEN:
591 fireSessionConnectionRefused();
592 throw new AuthorizationException( "Access denied to: " + url );
593
594 case HttpStatus.SC_UNAUTHORIZED:
595 fireSessionConnectionRefused();
596 throw new AuthorizationException( "Not authorized." );
597
598 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
599 fireSessionConnectionRefused();
600 throw new AuthorizationException( "Not authorized by proxy." );
601
602 case HttpStatus.SC_NOT_FOUND:
603 throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
604
605
606 default:
607 {
608 cleanupGetTransfer( resource );
609 TransferFailedException e =
610 new TransferFailedException( "Failed to transfer file: " + url + ". Return code is: "
611 + statusCode );
612 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
613 throw e;
614 }
615 }
616
617 InputStream is = null;
618
619 Header contentLengthHeader = getMethod.getResponseHeader( "Content-Length" );
620
621 if ( contentLengthHeader != null )
622 {
623 try
624 {
625 long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
626
627 resource.setContentLength( contentLength );
628 }
629 catch ( NumberFormatException e )
630 {
631 fireTransferDebug( "error parsing content length header '" + contentLengthHeader.getValue() + "' "
632 + e );
633 }
634 }
635
636 Header lastModifiedHeader = getMethod.getResponseHeader( "Last-Modified" );
637
638 long lastModified = 0;
639
640 if ( lastModifiedHeader != null )
641 {
642 try
643 {
644 lastModified = DateUtil.parseDate( lastModifiedHeader.getValue() ).getTime();
645
646 resource.setLastModified( lastModified );
647 }
648 catch ( DateParseException e )
649 {
650 fireTransferDebug( "Unable to parse last modified header" );
651 }
652
653 fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
654 }
655
656 Header contentEncoding = getMethod.getResponseHeader( "Content-Encoding" );
657 boolean isGZipped =
658 contentEncoding == null ? false : "gzip".equalsIgnoreCase( contentEncoding.getValue() );
659
660 try
661 {
662 is = getMethod.getResponseBodyAsStream();
663 if ( isGZipped )
664 {
665 is = new GZIPInputStream( is );
666 }
667 }
668 catch ( IOException e )
669 {
670 fireTransferError( resource, e, TransferEvent.REQUEST_GET );
671
672 String msg =
673 "Error occurred while retrieving from remote repository:" + getRepository() + ": " + e.getMessage();
674
675 throw new TransferFailedException( msg, e );
676 }
677
678 inputData.setInputStream( is );
679 }
680
681 protected void cleanupGetTransfer( Resource resource )
682 {
683 if ( getMethod != null )
684 {
685 getMethod.releaseConnection();
686 }
687 }
688
689 public void fillOutputData( OutputData outputData )
690 throws TransferFailedException
691 {
692 throw new IllegalStateException( "Should not be using the streaming wagon for HTTP PUT" );
693 }
694 }