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