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