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