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.commons.io.IOUtils;
23 import org.apache.maven.wagon.ConnectionException;
24 import org.apache.maven.wagon.InputData;
25 import org.apache.maven.wagon.OutputData;
26 import org.apache.maven.wagon.ResourceDoesNotExistException;
27 import org.apache.maven.wagon.StreamWagon;
28 import org.apache.maven.wagon.TransferFailedException;
29 import org.apache.maven.wagon.authentication.AuthenticationException;
30 import org.apache.maven.wagon.authorization.AuthorizationException;
31 import org.apache.maven.wagon.events.TransferEvent;
32 import org.apache.maven.wagon.proxy.ProxyInfo;
33 import org.apache.maven.wagon.resource.Resource;
34 import org.apache.maven.wagon.shared.http.EncodingUtil;
35 import org.apache.maven.wagon.shared.http.HtmlFileListParser;
36 import org.codehaus.plexus.util.Base64;
37
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.OutputStream;
42 import java.net.HttpURLConnection;
43 import java.net.InetSocketAddress;
44 import java.net.MalformedURLException;
45 import java.net.PasswordAuthentication;
46 import java.net.Proxy;
47 import java.net.Proxy.Type;
48 import java.net.SocketAddress;
49 import java.net.URL;
50 import java.util.ArrayList;
51 import java.util.List;
52 import java.util.Properties;
53 import java.util.zip.DeflaterInputStream;
54 import java.util.zip.GZIPInputStream;
55
56
57
58
59
60
61
62
63 public class LightweightHttpWagon
64 extends StreamWagon
65 {
66 private boolean preemptiveAuthentication;
67
68 private HttpURLConnection putConnection;
69
70 private Proxy proxy = Proxy.NO_PROXY;
71
72 public static final int MAX_REDIRECTS = 10;
73
74
75
76
77
78
79 private boolean useCache;
80
81
82
83
84 private Properties httpHeaders;
85
86
87
88
89 private volatile LightweightHttpWagonAuthenticator authenticator;
90
91
92
93
94
95
96
97 private String buildUrl( Resource resource )
98 {
99 return EncodingUtil.encodeURLToString( getRepository().getUrl(), resource.getName() );
100 }
101
102 public void fillInputData( InputData inputData )
103 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
104 {
105 Resource resource = inputData.getResource();
106
107 String visitingUrl = buildUrl( resource );
108 try
109 {
110 List<String> visitedUrls = new ArrayList<String>();
111
112 for ( int redirectCount = 0; redirectCount < MAX_REDIRECTS; redirectCount++ )
113 {
114 if ( visitedUrls.contains( visitingUrl ) )
115 {
116 throw new TransferFailedException( "Cyclic http redirect detected. Aborting! " + visitingUrl );
117 }
118 visitedUrls.add( visitingUrl );
119
120 URL url = new URL( visitingUrl );
121 HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection( this.proxy );
122
123 urlConnection.setRequestProperty( "Accept-Encoding", "gzip,deflate" );
124 if ( !useCache )
125 {
126 urlConnection.setRequestProperty( "Pragma", "no-cache" );
127 }
128
129 addHeaders( urlConnection );
130
131
132 int responseCode = urlConnection.getResponseCode();
133 if ( responseCode == HttpURLConnection.HTTP_FORBIDDEN
134 || responseCode == HttpURLConnection.HTTP_UNAUTHORIZED )
135 {
136 throw new AuthorizationException( "Access denied to: " + buildUrl( resource ) );
137 }
138 if ( responseCode == HttpURLConnection.HTTP_MOVED_PERM
139 || responseCode == HttpURLConnection.HTTP_MOVED_TEMP )
140 {
141 visitingUrl = urlConnection.getHeaderField( "Location" );
142 continue;
143 }
144
145 InputStream is = urlConnection.getInputStream();
146 String contentEncoding = urlConnection.getHeaderField( "Content-Encoding" );
147 boolean isGZipped = contentEncoding != null && "gzip".equalsIgnoreCase( contentEncoding );
148 if ( isGZipped )
149 {
150 is = new GZIPInputStream( is );
151 }
152 boolean isDeflated = contentEncoding != null && "deflate".equalsIgnoreCase( contentEncoding );
153 if ( isDeflated )
154 {
155 is = new DeflaterInputStream( is );
156 }
157 inputData.setInputStream( is );
158 resource.setLastModified( urlConnection.getLastModified() );
159 resource.setContentLength( urlConnection.getContentLength() );
160 break;
161 }
162 }
163 catch ( MalformedURLException e )
164 {
165 throw new ResourceDoesNotExistException( "Invalid repository URL: " + e.getMessage(), e );
166 }
167 catch ( FileNotFoundException e )
168 {
169 throw new ResourceDoesNotExistException( "Unable to locate resource in repository", e );
170 }
171 catch ( IOException e )
172 {
173 StringBuilder message = new StringBuilder( "Error transferring file: " );
174 message.append( e.getMessage() );
175 message.append( " from " + visitingUrl );
176 if ( getProxyInfo() != null && getProxyInfo().getHost() != null )
177 {
178 message.append( " with proxyInfo " ).append( getProxyInfo().toString() );
179 }
180 throw new TransferFailedException( message.toString(), e );
181 }
182 }
183
184 private void addHeaders( HttpURLConnection urlConnection )
185 {
186 if ( httpHeaders != null )
187 {
188 for ( Object header : httpHeaders.keySet() )
189 {
190 urlConnection.setRequestProperty( (String) header, httpHeaders.getProperty( (String) header ) );
191 }
192 }
193 setAuthorization( urlConnection );
194 }
195
196 private void setAuthorization( HttpURLConnection urlConnection )
197 {
198 if ( preemptiveAuthentication && authenticationInfo != null && authenticationInfo.getUserName() != null )
199 {
200 String credentials = authenticationInfo.getUserName() + ":" + authenticationInfo.getPassword();
201 String encoded = new String( Base64.encodeBase64( credentials.getBytes() ) );
202 urlConnection.setRequestProperty( "Authorization", "Basic " + encoded );
203 }
204 }
205
206 public void fillOutputData( OutputData outputData )
207 throws TransferFailedException
208 {
209 Resource resource = outputData.getResource();
210 try
211 {
212 URL url = new URL( buildUrl( resource ) );
213 putConnection = (HttpURLConnection) url.openConnection( this.proxy );
214
215 addHeaders( putConnection );
216
217 putConnection.setRequestMethod( "PUT" );
218 putConnection.setDoOutput( true );
219 outputData.setOutputStream( putConnection.getOutputStream() );
220 }
221 catch ( IOException e )
222 {
223 throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
224 }
225 }
226
227 protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
228 throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
229 {
230 try
231 {
232 int statusCode = putConnection.getResponseCode();
233
234 switch ( statusCode )
235 {
236
237 case HttpURLConnection.HTTP_OK:
238 case HttpURLConnection.HTTP_CREATED:
239 case HttpURLConnection.HTTP_ACCEPTED:
240 case HttpURLConnection.HTTP_NO_CONTENT:
241 break;
242
243 case HttpURLConnection.HTTP_FORBIDDEN:
244 throw new AuthorizationException( "Access denied to: " + buildUrl( resource ) );
245
246 case HttpURLConnection.HTTP_NOT_FOUND:
247 throw new ResourceDoesNotExistException( "File: " + buildUrl( resource ) + " does not exist" );
248
249
250 default:
251 throw new TransferFailedException(
252 "Failed to transfer file: " + buildUrl( resource ) + ". Return code is: " + statusCode );
253 }
254 }
255 catch ( IOException e )
256 {
257 fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
258
259 throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
260 }
261 }
262
263 protected void openConnectionInternal()
264 throws ConnectionException, AuthenticationException
265 {
266 final ProxyInfo proxyInfo = getProxyInfo( "http", getRepository().getHost() );
267 if ( proxyInfo != null )
268 {
269 this.proxy = getProxy( proxyInfo );
270 this.proxyInfo = proxyInfo;
271 }
272 authenticator.setWagon( this );
273
274 boolean usePreemptiveAuthentication =
275 Boolean.getBoolean( "maven.wagon.http.preemptiveAuthentication" ) || Boolean.parseBoolean(
276 repository.getParameter( "preemptiveAuthentication" ) ) || this.preemptiveAuthentication;
277
278 setPreemptiveAuthentication( usePreemptiveAuthentication );
279 }
280
281 @SuppressWarnings( "deprecation" )
282 public PasswordAuthentication requestProxyAuthentication()
283 {
284 if ( proxyInfo != null && proxyInfo.getUserName() != null )
285 {
286 String password = "";
287 if ( proxyInfo.getPassword() != null )
288 {
289 password = proxyInfo.getPassword();
290 }
291 return new PasswordAuthentication( proxyInfo.getUserName(), password.toCharArray() );
292 }
293 return null;
294 }
295
296 public PasswordAuthentication requestServerAuthentication()
297 {
298 if ( authenticationInfo != null && authenticationInfo.getUserName() != null )
299 {
300 String password = "";
301 if ( authenticationInfo.getPassword() != null )
302 {
303 password = authenticationInfo.getPassword();
304 }
305 return new PasswordAuthentication( authenticationInfo.getUserName(), password.toCharArray() );
306 }
307 return null;
308 }
309
310 private Proxy getProxy( ProxyInfo proxyInfo )
311 {
312 return new Proxy( getProxyType( proxyInfo ), getSocketAddress( proxyInfo ) );
313 }
314
315 private Type getProxyType( ProxyInfo proxyInfo )
316 {
317 if ( ProxyInfo.PROXY_SOCKS4.equals( proxyInfo.getType() ) || ProxyInfo.PROXY_SOCKS5.equals(
318 proxyInfo.getType() ) )
319 {
320 return Type.SOCKS;
321 }
322 else
323 {
324 return Type.HTTP;
325 }
326 }
327
328 public SocketAddress getSocketAddress( ProxyInfo proxyInfo )
329 {
330 return InetSocketAddress.createUnresolved( proxyInfo.getHost(), proxyInfo.getPort() );
331 }
332
333 public void closeConnection()
334 throws ConnectionException
335 {
336
337 if ( putConnection != null )
338 {
339 putConnection.disconnect();
340 }
341 authenticator.resetWagon();
342 }
343
344 public List<String> getFileList( String destinationDirectory )
345 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
346 {
347 InputData inputData = new InputData();
348
349 if ( destinationDirectory.length() > 0 && !destinationDirectory.endsWith( "/" ) )
350 {
351 destinationDirectory += "/";
352 }
353
354 String url = buildUrl( new Resource( destinationDirectory ) );
355
356 Resource resource = new Resource( destinationDirectory );
357
358 inputData.setResource( resource );
359
360 fillInputData( inputData );
361
362 InputStream is = inputData.getInputStream();
363
364 try
365 {
366
367 if ( is == null )
368 {
369 throw new TransferFailedException(
370 url + " - Could not open input stream for resource: '" + resource + "'" );
371 }
372
373 final List<String> htmlFileList = HtmlFileListParser.parseFileList( url, is );
374 is.close();
375 is = null;
376 return htmlFileList;
377 }
378 catch ( final IOException e )
379 {
380 throw new TransferFailedException( "Failure transferring " + resource.getName(), e );
381 }
382 finally
383 {
384 IOUtils.closeQuietly( is );
385 }
386 }
387
388 public boolean resourceExists( String resourceName )
389 throws TransferFailedException, AuthorizationException
390 {
391 HttpURLConnection headConnection;
392
393 try
394 {
395 Resource resource = new Resource( resourceName );
396 URL url = new URL( buildUrl( resource ) );
397 headConnection = (HttpURLConnection) url.openConnection( this.proxy );
398
399 addHeaders( headConnection );
400
401 headConnection.setRequestMethod( "HEAD" );
402 headConnection.setDoOutput( true );
403
404 int statusCode = headConnection.getResponseCode();
405
406 switch ( statusCode )
407 {
408 case HttpURLConnection.HTTP_OK:
409 return true;
410
411 case HttpURLConnection.HTTP_FORBIDDEN:
412 throw new AuthorizationException( "Access denied to: " + url );
413
414 case HttpURLConnection.HTTP_NOT_FOUND:
415 return false;
416
417 case HttpURLConnection.HTTP_UNAUTHORIZED:
418 throw new AuthorizationException( "Access denied to: " + url );
419
420 default:
421 throw new TransferFailedException(
422 "Failed to look for file: " + buildUrl( resource ) + ". Return code is: " + statusCode );
423 }
424 }
425 catch ( IOException e )
426 {
427 throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
428 }
429 }
430
431 public boolean isUseCache()
432 {
433 return useCache;
434 }
435
436 public void setUseCache( boolean useCache )
437 {
438 this.useCache = useCache;
439 }
440
441 public Properties getHttpHeaders()
442 {
443 return httpHeaders;
444 }
445
446 public void setHttpHeaders( Properties httpHeaders )
447 {
448 this.httpHeaders = httpHeaders;
449 }
450
451 void setSystemProperty( String key, String value )
452 {
453 if ( value != null )
454 {
455 System.setProperty( key, value );
456 }
457 else
458 {
459 System.getProperties().remove( key );
460 }
461 }
462
463 public void setPreemptiveAuthentication( boolean preemptiveAuthentication )
464 {
465 this.preemptiveAuthentication = preemptiveAuthentication;
466 }
467
468 public LightweightHttpWagonAuthenticator getAuthenticator()
469 {
470 return authenticator;
471 }
472
473 public void setAuthenticator( LightweightHttpWagonAuthenticator authenticator )
474 {
475 this.authenticator = authenticator;
476 }
477 }