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 }
265 authenticator.setWagon( this );
266
267 boolean usePreemptiveAuthentication =
268 Boolean.getBoolean( "maven.wagon.http.preemptiveAuthentication" ) || Boolean.parseBoolean(
269 repository.getParameter( "preemptiveAuthentication" ) ) || this.preemptiveAuthentication;
270
271 setPreemptiveAuthentication( usePreemptiveAuthentication );
272 }
273
274 @SuppressWarnings( "deprecation" )
275 public PasswordAuthentication requestProxyAuthentication()
276 {
277 if ( proxyInfo != null && proxyInfo.getUserName() != null )
278 {
279 String password = "";
280 if ( proxyInfo.getPassword() != null )
281 {
282 password = proxyInfo.getPassword();
283 }
284 return new PasswordAuthentication( proxyInfo.getUserName(), password.toCharArray() );
285 }
286 return null;
287 }
288
289 public PasswordAuthentication requestServerAuthentication()
290 {
291 if ( authenticationInfo != null && authenticationInfo.getUserName() != null )
292 {
293 String password = "";
294 if ( authenticationInfo.getPassword() != null )
295 {
296 password = authenticationInfo.getPassword();
297 }
298 return new PasswordAuthentication( authenticationInfo.getUserName(), password.toCharArray() );
299 }
300 return null;
301 }
302
303 private Proxy getProxy( ProxyInfo proxyInfo )
304 {
305 return new Proxy( getProxyType( proxyInfo ), getSocketAddress( proxyInfo ) );
306 }
307
308 private Type getProxyType( ProxyInfo proxyInfo )
309 {
310 if ( ProxyInfo.PROXY_SOCKS4.equals( proxyInfo.getType() ) || ProxyInfo.PROXY_SOCKS5.equals(
311 proxyInfo.getType() ) )
312 {
313 return Type.SOCKS;
314 }
315 else
316 {
317 return Type.HTTP;
318 }
319 }
320
321 public SocketAddress getSocketAddress( ProxyInfo proxyInfo )
322 {
323 return InetSocketAddress.createUnresolved( proxyInfo.getHost(), proxyInfo.getPort() );
324 }
325
326 public void closeConnection()
327 throws ConnectionException
328 {
329
330 if ( putConnection != null )
331 {
332 putConnection.disconnect();
333 }
334 authenticator.resetWagon();
335 }
336
337 public List<String> getFileList( String destinationDirectory )
338 throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
339 {
340 InputData inputData = new InputData();
341
342 if ( destinationDirectory.length() > 0 && !destinationDirectory.endsWith( "/" ) )
343 {
344 destinationDirectory += "/";
345 }
346
347 String url = buildUrl( new Resource( destinationDirectory ) );
348
349 Resource resource = new Resource( destinationDirectory );
350
351 inputData.setResource( resource );
352
353 fillInputData( inputData );
354
355 InputStream is = inputData.getInputStream();
356
357 try
358 {
359
360 if ( is == null )
361 {
362 throw new TransferFailedException(
363 url + " - Could not open input stream for resource: '" + resource + "'" );
364 }
365
366 return HtmlFileListParser.parseFileList( url, is );
367 }
368 finally
369 {
370 IOUtils.closeQuietly( is );
371 }
372 }
373
374 public boolean resourceExists( String resourceName )
375 throws TransferFailedException, AuthorizationException
376 {
377 HttpURLConnection headConnection;
378
379 try
380 {
381 Resource resource = new Resource( resourceName );
382 URL url = new URL( buildUrl( resource ) );
383 headConnection = (HttpURLConnection) url.openConnection( this.proxy );
384
385 addHeaders( headConnection );
386
387 headConnection.setRequestMethod( "HEAD" );
388 headConnection.setDoOutput( true );
389
390 int statusCode = headConnection.getResponseCode();
391
392 switch ( statusCode )
393 {
394 case HttpURLConnection.HTTP_OK:
395 return true;
396
397 case HttpURLConnection.HTTP_FORBIDDEN:
398 throw new AuthorizationException( "Access denied to: " + url );
399
400 case HttpURLConnection.HTTP_NOT_FOUND:
401 return false;
402
403 case HttpURLConnection.HTTP_UNAUTHORIZED:
404 throw new AuthorizationException( "Access denied to: " + url );
405
406 default:
407 throw new TransferFailedException(
408 "Failed to look for file: " + buildUrl( resource ) + ". Return code is: " + statusCode );
409 }
410 }
411 catch ( IOException e )
412 {
413 throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
414 }
415 }
416
417 public boolean isUseCache()
418 {
419 return useCache;
420 }
421
422 public void setUseCache( boolean useCache )
423 {
424 this.useCache = useCache;
425 }
426
427 public Properties getHttpHeaders()
428 {
429 return httpHeaders;
430 }
431
432 public void setHttpHeaders( Properties httpHeaders )
433 {
434 this.httpHeaders = httpHeaders;
435 }
436
437 void setSystemProperty( String key, String value )
438 {
439 if ( value != null )
440 {
441 System.setProperty( key, value );
442 }
443 else
444 {
445 System.getProperties().remove( key );
446 }
447 }
448
449 public void setPreemptiveAuthentication( boolean preemptiveAuthentication )
450 {
451 this.preemptiveAuthentication = preemptiveAuthentication;
452 }
453
454 public LightweightHttpWagonAuthenticator getAuthenticator()
455 {
456 return authenticator;
457 }
458
459 public void setAuthenticator( LightweightHttpWagonAuthenticator authenticator )
460 {
461 this.authenticator = authenticator;
462 }
463 }