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