[WAGON-488] upgrade upgrade webdav wagon to a more recent httpclient version
Signed-off-by: olivier lamy <ol...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/maven-wagon/repo Commit: http://git-wip-us.apache.org/repos/asf/maven-wagon/commit/2a129274 Tree: http://git-wip-us.apache.org/repos/asf/maven-wagon/tree/2a129274 Diff: http://git-wip-us.apache.org/repos/asf/maven-wagon/diff/2a129274 Branch: refs/heads/feature/webdav_upgrade Commit: 2a129274c0ce85fd61e466734fe98dc9badacef5 Parents: 37dfe3a Author: olivier lamy <ol...@apache.org> Authored: Fri Jun 30 21:26:08 2017 +1000 Committer: olivier lamy <ol...@apache.org> Committed: Fri Jun 30 21:26:08 2017 +1000 ---------------------------------------------------------------------- pom.xml | 8 +- wagon-providers/wagon-http-shared/pom.xml | 14 + .../shared/http/AbstractHttpClientWagon.java | 1152 ++++++++++++++++++ .../maven/wagon/shared/http/BasicAuthScope.java | 168 +++ .../wagon/shared/http/ConfigurationUtils.java | 233 ++++ .../wagon/shared/http/HttpConfiguration.java | 106 ++ .../shared/http/HttpMethodConfiguration.java | 174 +++ .../wagon/shared/http/RelaxedTrustStrategy.java | 76 ++ wagon-providers/wagon-http/pom.xml | 8 +- .../providers/http/AbstractHttpClientWagon.java | 1148 ----------------- .../wagon/providers/http/BasicAuthScope.java | 168 --- .../providers/http/ConfigurationUtils.java | 230 ---- .../wagon/providers/http/HttpConfiguration.java | 106 -- .../providers/http/HttpMethodConfiguration.java | 174 --- .../maven/wagon/providers/http/HttpWagon.java | 1 + .../providers/http/RelaxedTrustStrategy.java | 76 -- .../http/AbstractHttpClientWagonTest.java | 1 + .../providers/http/BasicAuthScopeTest.java | 1 + .../providers/http/HttpClientWagonTest.java | 6 + .../providers/http/HttpWagonPreemptiveTest.java | 2 + .../wagon/providers/http/HttpWagonTest.java | 2 + .../providers/http/HttpWagonTimeoutTest.java | 2 + .../http/HttpsWagonPreemptiveTest.java | 2 + .../wagon/providers/http/HttpsWagonTest.java | 10 +- wagon-providers/wagon-webdav-jackrabbit/pom.xml | 41 +- .../webdav/client/methods/XmlRequestEntity.java | 23 +- .../webdav/AbstractHttpClientWagon.java | 831 ------------- .../providers/webdav/HttpConfiguration.java | 107 -- .../webdav/HttpMethodConfiguration.java | 319 ----- .../wagon/providers/webdav/WebDavWagon.java | 83 +- .../providers/webdav/HttpClientWagonTest.java | 112 +- .../wagon/providers/webdav/WebDavWagonTest.java | 12 +- .../providers/webdav/WebDavsWagonTest.java | 10 +- 33 files changed, 2112 insertions(+), 3294 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 0930036..48aff04 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,8 @@ under the License. <maven.test.redirectTestOutputToFile>true</maven.test.redirectTestOutputToFile> <slf4jVersion>1.7.22</slf4jVersion> <maven.site.path>wagon-archives/wagon-LATEST</maven.site.path> + <maven.compiler.source>1.7</maven.compiler.source> + <maven.compiler.target>1.7</maven.compiler.target> </properties> <contributors> @@ -406,13 +408,13 @@ under the License. <configuration> <signature> <groupId>org.codehaus.mojo.signature</groupId> - <artifactId>java15</artifactId> + <artifactId>java17</artifactId> <version>1.0</version> </signature> </configuration> <executions> <execution> - <id>check-java-1.5-compat</id> + <id>check-java-1.7-compat</id> <phase>process-classes</phase> <goals> <goal>check</goal> @@ -431,7 +433,7 @@ under the License. <configuration> <rules> <requireJavaVersion> - <version>1.6.0</version> + <version>1.7.0</version> </requireJavaVersion> </rules> </configuration> http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/pom.xml ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/pom.xml b/wagon-providers/wagon-http-shared/pom.xml index aa194e6..441f30d 100644 --- a/wagon-providers/wagon-http-shared/pom.xml +++ b/wagon-providers/wagon-http-shared/pom.xml @@ -40,6 +40,20 @@ under the License. <version>1.7.2</version> </dependency> <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <exclusions> + <exclusion> + <artifactId>commons-logging</artifactId> + <groupId>commons-logging</groupId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore</artifactId> + </dependency> + <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java new file mode 100755 index 0000000..f183f9b --- /dev/null +++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/AbstractHttpClientWagon.java @@ -0,0 +1,1152 @@ +package org.apache.maven.wagon.shared.http; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpException; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.ChallengeState; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.NTCredentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.AuthCache; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.DateUtils; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContextBuilder; +import org.apache.http.conn.ssl.SSLInitializationException; +import org.apache.http.entity.AbstractHttpEntity; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.BasicAuthCache; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; +import org.apache.http.util.EntityUtils; +import org.apache.maven.wagon.InputData; +import org.apache.maven.wagon.OutputData; +import org.apache.maven.wagon.PathUtils; +import org.apache.maven.wagon.ResourceDoesNotExistException; +import org.apache.maven.wagon.StreamWagon; +import org.apache.maven.wagon.TransferFailedException; +import org.apache.maven.wagon.Wagon; +import org.apache.maven.wagon.authorization.AuthorizationException; +import org.apache.maven.wagon.events.TransferEvent; +import org.apache.maven.wagon.proxy.ProxyInfo; +import org.apache.maven.wagon.repository.Repository; +import org.apache.maven.wagon.resource.Resource; +import org.codehaus.plexus.util.IOUtil; +import org.codehaus.plexus.util.StringUtils; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; + +/** + * @author <a href="michal.mac...@dimatics.com">Michal Maczka</a> + * @author <a href="mailto:ja...@atlassian.com">James William Dumay</a> + */ +public abstract class AbstractHttpClientWagon + extends StreamWagon +{ + private final class RequestEntityImplementation + extends AbstractHttpEntity + { + + private static final int BUFFER_SIZE = 2048; + + private final Resource resource; + + private final Wagon wagon; + + private ByteBuffer byteBuffer; + + private File source; + + private long length = -1; + + private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon, + final File source ) + throws TransferFailedException + { + if ( source != null ) + { + this.source = source; + } + else + { + try + { + byte[] bytes = IOUtil.toByteArray( stream ); + byteBuffer = ByteBuffer.allocate( bytes.length ); + byteBuffer.put( bytes ); + stream.close(); + } + catch ( IOException e ) + { + throw new TransferFailedException( e.getMessage(), e ); + } + finally + { + IOUtil.close( stream ); + } + } + this.resource = resource; + this.length = resource == null ? -1 : resource.getContentLength(); + + this.wagon = wagon; + } + + public long getContentLength() + { + return length; + } + + public InputStream getContent() + throws IOException, IllegalStateException + { + if ( this.source != null ) + { + return new FileInputStream( this.source ); + } + return new ByteArrayInputStream( this.byteBuffer.array() ); + } + + public boolean isRepeatable() + { + return true; + } + + public void writeTo( final OutputStream outputStream ) + throws IOException + { + if ( outputStream == null ) + { + throw new NullPointerException( "outputStream cannot be null" ); + } + TransferEvent transferEvent = + new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT ); + transferEvent.setTimestamp( System.currentTimeMillis() ); + InputStream instream = ( this.source != null ) + ? new FileInputStream( this.source ) + : new ByteArrayInputStream( this.byteBuffer.array() ); + try + { + byte[] buffer = new byte[BUFFER_SIZE]; + int l; + if ( this.length < 0 ) + { + // until EOF + while ( ( l = instream.read( buffer ) ) != -1 ) + { + fireTransferProgress( transferEvent, buffer, -1 ); + outputStream.write( buffer, 0, l ); + } + } + else + { + // no need to consume more than length + long remaining = this.length; + while ( remaining > 0 ) + { + l = instream.read( buffer, 0, (int) Math.min( BUFFER_SIZE, remaining ) ); + if ( l == -1 ) + { + break; + } + fireTransferProgress( transferEvent, buffer, (int) Math.min( BUFFER_SIZE, remaining ) ); + outputStream.write( buffer, 0, l ); + remaining -= l; + } + } + } + finally + { + instream.close(); + } + } + + public boolean isStreaming() + { + return true; + } + } + + private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" ); + + /** + * use http(s) connection pool mechanism. + * <b>enabled by default</b> + */ + private static boolean persistentPool = + Boolean.valueOf( System.getProperty( "maven.wagon.http.pool", "true" ) ); + + /** + * skip failure on certificate validity checks. + * <b>disabled by default</b> + */ + private static final boolean SSL_INSECURE = + Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.insecure", "false" ) ); + + /** + * if using sslInsecure, certificate date issues will be ignored + * <b>disabled by default</b> + */ + private static final boolean IGNORE_SSL_VALIDITY_DATES = + Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.ignore.validity.dates", "false" ) ); + + /** + * If enabled, ssl hostname verifier does not check hostname. Disable this will use a browser compat hostname + * verifier <b>disabled by default</b> + */ + private static final boolean SSL_ALLOW_ALL = + Boolean.valueOf( System.getProperty( "maven.wagon.http.ssl.allowall", "false" ) ); + + + /** + * Maximum concurrent connections per distinct route. + * <b>20 by default</b> + */ + private static final int MAX_CONN_PER_ROUTE = + Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxPerRoute", "20" ) ); + + /** + * Maximum concurrent connections in total. + * <b>40 by default</b> + */ + private static final int MAX_CONN_TOTAL = + Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxTotal", "40" ) ); + + /** + * Internal connection manager + */ + private static HttpClientConnectionManager httpClientConnectionManager = createConnManager(); + + + /** + * See RFC6585 + */ + protected static final int SC_TOO_MANY_REQUESTS = 429; + + /** + * For exponential backoff. + */ + + /** + * Initial seconds to back off when a HTTP 429 received. + * Subsequent 429 responses result in exponental backoff. + * <b>5 by default</b> + * + * @since 2.7 + */ + private int initialBackoffSeconds = + Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.backoffSeconds", "5" ) ); + + /** + * The maximum amount of time we want to back off in the case of + * repeated HTTP 429 response codes. + * + * @since 2.7 + */ + private static final int MAX_BACKOFF_WAIT_SECONDS = + Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxBackoffSeconds", "180" ) ); + + + protected int backoff( int wait, String url ) + throws InterruptedException, TransferFailedException + { + TimeUnit.SECONDS.sleep( wait ); + int nextWait = wait * 2; + if ( nextWait >= getMaxBackoffWaitSeconds() ) + { + throw new TransferFailedException( + "Waited too long to access: " + url + ". Return code is: " + SC_TOO_MANY_REQUESTS ); + } + return nextWait; + } + + @SuppressWarnings( "checkstyle:linelength" ) + private static PoolingHttpClientConnectionManager createConnManager() + { + + String sslProtocolsStr = System.getProperty( "https.protocols" ); + String cipherSuitesStr = System.getProperty( "https.cipherSuites" ); + String[] sslProtocols = sslProtocolsStr != null ? sslProtocolsStr.split( " *, *" ) : null; + String[] cipherSuites = cipherSuitesStr != null ? cipherSuitesStr.split( " *, *" ) : null; + + SSLConnectionSocketFactory sslConnectionSocketFactory; + if ( SSL_INSECURE ) + { + try + { + SSLContext sslContext = new SSLContextBuilder().useSSL().loadTrustMaterial( null, + new RelaxedTrustStrategy( + IGNORE_SSL_VALIDITY_DATES ) ).build(); + sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslContext, sslProtocols, cipherSuites, + SSL_ALLOW_ALL + ? SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER + : SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER ); + } + catch ( Exception ex ) + { + throw new SSLInitializationException( ex.getMessage(), ex ); + } + } + else + { + sslConnectionSocketFactory = + new SSLConnectionSocketFactory( HttpsURLConnection.getDefaultSSLSocketFactory(), sslProtocols, + cipherSuites, + SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER ); + } + + Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register( "http", + PlainConnectionSocketFactory.INSTANCE ).register( + "https", sslConnectionSocketFactory ).build(); + + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( registry ); + if ( persistentPool ) + { + connManager.setDefaultMaxPerRoute( MAX_CONN_PER_ROUTE ); + connManager.setMaxTotal( MAX_CONN_TOTAL ); + } + else + { + connManager.setMaxTotal( 1 ); + } + return connManager; + } + + private static CloseableHttpClient httpClient = createClient(); + + private static CloseableHttpClient createClient() + { + return HttpClientBuilder.create() // + .useSystemProperties() // + .disableConnectionState() // + .setConnectionManager( httpClientConnectionManager ) // + .build(); + } + + private CredentialsProvider credentialsProvider; + + private AuthCache authCache; + + private Closeable closeable; + + /** + * @plexus.configuration + * @deprecated Use httpConfiguration instead. + */ + private Properties httpHeaders; + + /** + * @since 1.0-beta-6 + */ + private HttpConfiguration httpConfiguration; + + /** + * Basic auth scope overrides + * @since 2.8 + */ + private BasicAuthScope basicAuth; + + /** + * Proxy basic auth scope overrides + * @since 2.8 + */ + private BasicAuthScope proxyAuth; + + public void openConnectionInternal() + { + repository.setUrl( getURL( repository ) ); + + credentialsProvider = new BasicCredentialsProvider(); + authCache = new BasicAuthCache(); + + if ( authenticationInfo != null ) + { + + String username = authenticationInfo.getUserName(); + String password = authenticationInfo.getPassword(); + + if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) ) + { + Credentials creds = new UsernamePasswordCredentials( username, password ); + + String host = getRepository().getHost(); + int port = getRepository().getPort(); + + credentialsProvider.setCredentials( getBasicAuthScope().getScope( host, port ), creds ); + } + } + + ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() ); + if ( proxyInfo != null ) + { + String proxyUsername = proxyInfo.getUserName(); + String proxyPassword = proxyInfo.getPassword(); + String proxyHost = proxyInfo.getHost(); + String proxyNtlmHost = proxyInfo.getNtlmHost(); + String proxyNtlmDomain = proxyInfo.getNtlmDomain(); + if ( proxyHost != null ) + { + if ( proxyUsername != null && proxyPassword != null ) + { + Credentials creds; + if ( proxyNtlmHost != null || proxyNtlmDomain != null ) + { + creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain ); + } + else + { + creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword ); + } + + int proxyPort = proxyInfo.getPort(); + + AuthScope authScope = getProxyBasicAuthScope().getScope( proxyHost, proxyPort ); + credentialsProvider.setCredentials( authScope, creds ); + } + } + } + } + + public void closeConnection() + { + if ( !persistentPool ) + { + httpClientConnectionManager.closeIdleConnections( 0, TimeUnit.MILLISECONDS ); + } + + if ( authCache != null ) + { + authCache.clear(); + authCache = null; + } + + if ( credentialsProvider != null ) + { + credentialsProvider.clear(); + credentialsProvider = null; + } + } + + public static CloseableHttpClient getHttpClient() + { + return httpClient; + } + + public static void setPersistentPool( boolean persistentPool ) + { + persistentPool = persistentPool; + } + + public static void setPoolingHttpClientConnectionManager( + PoolingHttpClientConnectionManager poolingHttpClientConnectionManager ) + { + httpClientConnectionManager = poolingHttpClientConnectionManager; + httpClient = createClient(); + } + + public void put( File source, String resourceName ) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException + { + Resource resource = new Resource( resourceName ); + + firePutInitiated( resource, source ); + + resource.setContentLength( source.length() ); + + resource.setLastModified( source.lastModified() ); + + put( null, resource, source ); + } + + public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified ) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException + { + Resource resource = new Resource( destination ); + + firePutInitiated( resource, null ); + + resource.setContentLength( contentLength ); + + resource.setLastModified( lastModified ); + + put( stream, resource, null ); + } + + private void put( final InputStream stream, Resource resource, File source ) + throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException + { + put( resource, source, new RequestEntityImplementation( stream, resource, this, source ) ); + } + + private void put( Resource resource, File source, HttpEntity httpEntity ) + throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException + { + put( resource, source, httpEntity, buildUrl( resource ) ); + } + + /** + * Builds a complete URL string from the repository URL and the relative path of the resource passed. + * + * @param resource the resource to extract the relative path from. + * @return the complete URL + */ + private String buildUrl( Resource resource ) + { + return EncodingUtil.encodeURLToString( getRepository().getUrl(), resource.getName() ); + } + + + private void put( Resource resource, File source, HttpEntity httpEntity, String url ) + throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException + { + put( getInitialBackoffSeconds(), resource, source, httpEntity, url ); + } + + + private void put( int wait, Resource resource, File source, HttpEntity httpEntity, String url ) + throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException + { + + //Parent directories need to be created before posting + try + { + mkdirs( PathUtils.dirname( resource.getName() ) ); + } + catch ( HttpException he ) + { + fireTransferError( resource, he, TransferEvent.REQUEST_PUT ); + } + catch ( IOException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); + } + + // preemptive for put + // TODO: is it a good idea, though? 'Expect-continue' handshake would serve much better + + Repository repo = getRepository(); + HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() ); + AuthScope targetScope = getBasicAuthScope().getScope( targetHost ); + + if ( credentialsProvider.getCredentials( targetScope ) != null ) + { + BasicScheme targetAuth = new BasicScheme(); + authCache.put( targetHost, targetAuth ); + } + + HttpPut putMethod = new HttpPut( url ); + + firePutStarted( resource, source ); + + try + { + putMethod.setEntity( httpEntity ); + + CloseableHttpResponse response = execute( putMethod ); + try + { + int statusCode = response.getStatusLine().getStatusCode(); + String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + "."; + fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase ); + + // Check that we didn't run out of retries. + switch ( statusCode ) + { + // Success Codes + case HttpStatus.SC_OK: // 200 + case HttpStatus.SC_CREATED: // 201 + case HttpStatus.SC_ACCEPTED: // 202 + case HttpStatus.SC_NO_CONTENT: // 204 + break; + // handle all redirect even if http specs says " the user agent MUST NOT automatically redirect + // the request unless it can be confirmed by the user" + case HttpStatus.SC_MOVED_PERMANENTLY: // 301 + case HttpStatus.SC_MOVED_TEMPORARILY: // 302 + case HttpStatus.SC_SEE_OTHER: // 303 + put( resource, source, httpEntity, calculateRelocatedUrl( response ) ); + return; + case HttpStatus.SC_FORBIDDEN: + fireSessionConnectionRefused(); + throw new AuthorizationException( "Access denied to: " + url + reasonPhrase ); + + case HttpStatus.SC_NOT_FOUND: + throw new ResourceDoesNotExistException( "File: " + url + " does not exist" + reasonPhrase ); + + case SC_TOO_MANY_REQUESTS: + put( backoff( wait, url ), resource, source, httpEntity, url ); + break; + //add more entries here + default: + TransferFailedException e = new TransferFailedException( + "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase ); + fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); + throw e; + } + + firePutCompleted( resource, source ); + + EntityUtils.consume( response.getEntity() ); + } + finally + { + response.close(); + } + } + catch ( IOException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); + + throw new TransferFailedException( e.getMessage(), e ); + } + catch ( HttpException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); + + throw new TransferFailedException( e.getMessage(), e ); + } + catch ( InterruptedException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_PUT ); + + throw new TransferFailedException( e.getMessage(), e ); + } + + } + + protected String calculateRelocatedUrl( HttpResponse response ) + { + Header locationHeader = response.getFirstHeader( "Location" ); + String locationField = locationHeader.getValue(); + // is it a relative Location or a full ? + return locationField.startsWith( "http" ) ? locationField : getURL( getRepository() ) + '/' + locationField; + } + + protected void mkdirs( String dirname ) + throws HttpException, IOException + { + // nothing to do + } + + public boolean resourceExists( String resourceName ) + throws TransferFailedException, AuthorizationException + { + return resourceExists( getInitialBackoffSeconds(), resourceName ); + } + + + private boolean resourceExists( int wait, String resourceName ) + throws TransferFailedException, AuthorizationException + { + String repositoryUrl = getRepository().getUrl(); + String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resourceName; + HttpHead headMethod = new HttpHead( url ); + try + { + CloseableHttpResponse response = execute( headMethod ); + try + { + int statusCode = response.getStatusLine().getStatusCode(); + String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + "."; + boolean result; + switch ( statusCode ) + { + case HttpStatus.SC_OK: + result = true; + break; + case HttpStatus.SC_NOT_MODIFIED: + result = true; + break; + case HttpStatus.SC_FORBIDDEN: + throw new AuthorizationException( "Access denied to: " + url + reasonPhrase ); + + case HttpStatus.SC_UNAUTHORIZED: + throw new AuthorizationException( "Not authorized " + reasonPhrase ); + + case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: + throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase ); + + case HttpStatus.SC_NOT_FOUND: + result = false; + break; + + case SC_TOO_MANY_REQUESTS: + return resourceExists( backoff( wait, resourceName ), resourceName ); + + //add more entries here + default: + throw new TransferFailedException( + "Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase ); + } + + EntityUtils.consume( response.getEntity() ); + return result; + } + finally + { + response.close(); + } + } + catch ( IOException e ) + { + throw new TransferFailedException( e.getMessage(), e ); + } + catch ( HttpException e ) + { + throw new TransferFailedException( e.getMessage(), e ); + } + catch ( InterruptedException e ) + { + throw new TransferFailedException( e.getMessage(), e ); + } + + } + + protected CloseableHttpResponse execute( HttpUriRequest httpMethod ) + throws HttpException, IOException + { + setHeaders( httpMethod ); + String userAgent = getUserAgent( httpMethod ); + if ( userAgent != null ) + { + httpMethod.setHeader( HTTP.USER_AGENT, userAgent ); + } + + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + // WAGON-273: default the cookie-policy to browser compatible + requestConfigBuilder.setCookieSpec( CookieSpecs.BROWSER_COMPATIBILITY ); + + Repository repo = getRepository(); + ProxyInfo proxyInfo = getProxyInfo( repo.getProtocol(), repo.getHost() ); + if ( proxyInfo != null ) + { + HttpHost proxy = new HttpHost( proxyInfo.getHost(), proxyInfo.getPort() ); + requestConfigBuilder.setProxy( proxy ); + } + + HttpMethodConfiguration config = + httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( httpMethod ); + + if ( config != null ) + { + ConfigurationUtils.copyConfig( config, requestConfigBuilder ); + } + else + { + requestConfigBuilder.setSocketTimeout( getReadTimeout() ); + if ( httpMethod instanceof HttpPut ) + { + requestConfigBuilder.setExpectContinueEnabled( true ); + } + } + + if ( httpMethod instanceof HttpPut ) + { + requestConfigBuilder.setRedirectsEnabled( false ); + } + + HttpClientContext localContext = HttpClientContext.create(); + localContext.setCredentialsProvider( credentialsProvider ); + localContext.setAuthCache( authCache ); + localContext.setRequestConfig( requestConfigBuilder.build() ); + + if ( config != null && config.isUsePreemptive() ) + { + HttpHost targetHost = new HttpHost( repo.getHost(), repo.getPort(), repo.getProtocol() ); + AuthScope targetScope = getBasicAuthScope().getScope( targetHost ); + + if ( credentialsProvider.getCredentials( targetScope ) != null ) + { + BasicScheme targetAuth = new BasicScheme(); + authCache.put( targetHost, targetAuth ); + } + } + + if ( proxyInfo != null ) + { + if ( proxyInfo.getHost() != null ) + { + HttpHost proxyHost = new HttpHost( proxyInfo.getHost(), proxyInfo.getPort() ); + AuthScope proxyScope = getProxyBasicAuthScope().getScope( proxyHost ); + + if ( credentialsProvider.getCredentials( proxyScope ) != null ) + { + /* This is extremely ugly because we need to set challengeState to PROXY, but + * the constructor is deprecated. Alternatively, we could subclass BasicScheme + * to ProxyBasicScheme and set the state internally in the constructor. + */ + BasicScheme proxyAuth = new BasicScheme( ChallengeState.PROXY ); + authCache.put( proxyHost, proxyAuth ); + } + } + } + + return httpClient.execute( httpMethod, localContext ); + } + + public void setHeaders( HttpUriRequest method ) + { + HttpMethodConfiguration config = + httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method ); + if ( config == null || config.isUseDefaultHeaders() ) + { + // TODO: merge with the other headers and have some better defaults, unify with lightweight headers + method.addHeader( "Cache-control", "no-cache" ); + method.addHeader( "Cache-store", "no-store" ); + method.addHeader( "Pragma", "no-cache" ); + method.addHeader( "Expires", "0" ); + method.addHeader( "Accept-Encoding", "gzip" ); + } + + if ( httpHeaders != null ) + { + for ( Map.Entry<Object, Object> entry : httpHeaders.entrySet() ) + { + method.setHeader( (String) entry.getKey(), (String) entry.getValue() ); + } + } + + Header[] headers = config == null ? null : config.asRequestHeaders(); + if ( headers != null ) + { + for ( Header header : headers ) + { + method.setHeader( header ); + } + } + } + + protected String getUserAgent( HttpUriRequest method ) + { + if ( httpHeaders != null ) + { + String value = (String) httpHeaders.get( "User-Agent" ); + if ( value != null ) + { + return value; + } + } + HttpMethodConfiguration config = + httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method ); + + if ( config != null ) + { + return (String) config.getHeaders().get( "User-Agent" ); + } + return null; + } + + /** + * getUrl + * Implementors can override this to remove unwanted parts of the url such as role-hints + * + * @param repository + * @return + */ + protected String getURL( Repository repository ) + { + return repository.getUrl(); + } + + public HttpConfiguration getHttpConfiguration() + { + return httpConfiguration; + } + + public void setHttpConfiguration( HttpConfiguration httpConfiguration ) + { + this.httpConfiguration = httpConfiguration; + } + + /** + * Get the override values for standard HttpClient AuthScope + * + * @return the basicAuth + */ + public BasicAuthScope getBasicAuthScope() + { + if ( basicAuth == null ) + { + basicAuth = new BasicAuthScope(); + } + return basicAuth; + } + + /** + * Set the override values for standard HttpClient AuthScope + * + * @param basicAuth the AuthScope to set + */ + public void setBasicAuthScope( BasicAuthScope basicAuth ) + { + this.basicAuth = basicAuth; + } + + /** + * Get the override values for proxy HttpClient AuthScope + * + * @return the proxyAuth + */ + public BasicAuthScope getProxyBasicAuthScope() + { + if ( proxyAuth == null ) + { + proxyAuth = new BasicAuthScope(); + } + return proxyAuth; + } + + /** + * Set the override values for proxy HttpClient AuthScope + * + * @param proxyAuth the AuthScope to set + */ + public void setProxyBasicAuthScope( BasicAuthScope proxyAuth ) + { + this.proxyAuth = proxyAuth; + } + + public void fillInputData( InputData inputData ) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException + { + fillInputData( getInitialBackoffSeconds(), inputData ); + } + + private void fillInputData( int wait, InputData inputData ) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException + { + Resource resource = inputData.getResource(); + + String repositoryUrl = getRepository().getUrl(); + String url = repositoryUrl + ( repositoryUrl.endsWith( "/" ) ? "" : "/" ) + resource.getName(); + HttpGet getMethod = new HttpGet( url ); + long timestamp = resource.getLastModified(); + if ( timestamp > 0 ) + { + SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US ); + fmt.setTimeZone( GMT_TIME_ZONE ); + Header hdr = new BasicHeader( "If-Modified-Since", fmt.format( new Date( timestamp ) ) ); + fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" ); + getMethod.addHeader( hdr ); + } + + try + { + CloseableHttpResponse response = execute( getMethod ); + closeable = response; + int statusCode = response.getStatusLine().getStatusCode(); + + String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + "."; + + fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase ); + + switch ( statusCode ) + { + case HttpStatus.SC_OK: + break; + + case HttpStatus.SC_NOT_MODIFIED: + // return, leaving last modified set to original value so getIfNewer should return unmodified + return; + case HttpStatus.SC_FORBIDDEN: + fireSessionConnectionRefused(); + throw new AuthorizationException( "Access denied to: " + url + " " + reasonPhrase ); + + case HttpStatus.SC_UNAUTHORIZED: + fireSessionConnectionRefused(); + throw new AuthorizationException( "Not authorized " + reasonPhrase ); + + case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED: + fireSessionConnectionRefused(); + throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase ); + + case HttpStatus.SC_NOT_FOUND: + throw new ResourceDoesNotExistException( "File: " + url + " " + reasonPhrase ); + + case SC_TOO_MANY_REQUESTS: + fillInputData( backoff( wait, url ), inputData ); + break; + + // add more entries here + default: + cleanupGetTransfer( resource ); + TransferFailedException e = new TransferFailedException( + "Failed to transfer file: " + url + ". Return code is: " + statusCode + " " + reasonPhrase ); + fireTransferError( resource, e, TransferEvent.REQUEST_GET ); + throw e; + } + + Header contentLengthHeader = response.getFirstHeader( "Content-Length" ); + + if ( contentLengthHeader != null ) + { + try + { + long contentLength = Long.parseLong( contentLengthHeader.getValue() ); + + resource.setContentLength( contentLength ); + } + catch ( NumberFormatException e ) + { + fireTransferDebug( + "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e ); + } + } + + Header lastModifiedHeader = response.getFirstHeader( "Last-Modified" ); + if ( lastModifiedHeader != null ) + { + Date lastModified = DateUtils.parseDate( lastModifiedHeader.getValue() ); + if ( lastModified != null ) + { + resource.setLastModified( lastModified.getTime() ); + fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + + lastModified.getTime() + ")" ); + } + } + + HttpEntity entity = response.getEntity(); + if ( entity != null ) + { + inputData.setInputStream( entity.getContent() ); + } + } + catch ( IOException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_GET ); + + throw new TransferFailedException( e.getMessage(), e ); + } + catch ( HttpException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_GET ); + + throw new TransferFailedException( e.getMessage(), e ); + } + catch ( InterruptedException e ) + { + fireTransferError( resource, e, TransferEvent.REQUEST_GET ); + + throw new TransferFailedException( e.getMessage(), e ); + } + + } + + protected void cleanupGetTransfer( Resource resource ) + { + if ( closeable != null ) + { + try + { + closeable.close(); + } + catch ( IOException ignore ) + { + // ignore + } + + } + } + + + @Override + public void putFromStream( InputStream stream, String destination ) + throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException + { + putFromStream( stream, destination, -1, -1 ); + } + + @Override + protected void putFromStream( InputStream stream, Resource resource ) + throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException + { + putFromStream( stream, resource.getName(), -1, -1 ); + } + + public Properties getHttpHeaders() + { + return httpHeaders; + } + + public void setHttpHeaders( Properties httpHeaders ) + { + this.httpHeaders = httpHeaders; + } + + @Override + public void fillOutputData( OutputData outputData ) + throws TransferFailedException + { + // no needed in this implementation but throw an Exception if used + throw new IllegalStateException( "this wagon http client must not use fillOutputData" ); + } + + public int getInitialBackoffSeconds() + { + return initialBackoffSeconds; + } + + public void setInitialBackoffSeconds( int initialBackoffSeconds ) + { + this.initialBackoffSeconds = initialBackoffSeconds; + } + + public static int getMaxBackoffWaitSeconds() + { + return MAX_BACKOFF_WAIT_SECONDS; + } +} http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/BasicAuthScope.java ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/BasicAuthScope.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/BasicAuthScope.java new file mode 100644 index 0000000..3060cbf --- /dev/null +++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/BasicAuthScope.java @@ -0,0 +1,168 @@ +package org.apache.maven.wagon.shared.http; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; + +/** + * @since 2.8 + */ +public class BasicAuthScope +{ + private String host; + + private String port; + + private String realm; + + /** + * @return the host + */ + public String getHost() + { + return host; + } + + /** + * @param host the host to set + */ + public void setHost( String host ) + { + this.host = host; + } + + /** + * @return the realm + */ + public String getRealm() + { + return realm; + } + + /** + * @param realm the realm to set + */ + public void setRealm( String realm ) + { + this.realm = realm; + } + + /** + * Create an authScope given the /repository/host and /repository/password + * and the /server/basicAuth or /server/proxyBasicAuth host, port and realm + * settings. The basicAuth setting should override the repository settings + * host and/or port if host, port or realm is set to "ANY". + * <p/> + * Realm can also be set to a specific string and will be set if + * /server/basicAuthentication/realm is non-null + * + * @param host The server setting's /server/host value + * @param port The server setting's /server/port value + * @return + */ + public AuthScope getScope( String host, int port ) + { + if ( getHost() != null // + && "ANY".compareTo( getHost() ) == 0 // + && getPort() != null // + && "ANY".compareTo( getPort() ) == 0 // + && getRealm() != null // + && "ANY".compareTo( getRealm() ) == 0 ) + { + return AuthScope.ANY; + } + String scopeHost = host; + if ( getHost() != null ) + { + if ( "ANY".compareTo( getHost() ) == 0 ) + { + scopeHost = AuthScope.ANY_HOST; + } + else + { + scopeHost = getHost(); + } + } + + int scopePort = port > -1 ? port : AuthScope.ANY_PORT; + // -1 for server/port settings does this, but providing an override here + // in + // the BasicAuthScope config + if ( getPort() != null ) + { + if ( "ANY".compareTo( getPort() ) == 0 ) + { + scopePort = AuthScope.ANY_PORT; + } + else + { + scopePort = Integer.parseInt( getPort() ); + } + } + + String scopeRealm = AuthScope.ANY_REALM; + if ( getRealm() != null ) + { + if ( "ANY".compareTo( getRealm() ) != 0 ) + { + scopeRealm = getRealm(); + } + else + { + scopeRealm = getRealm(); + } + } + + return new AuthScope( scopeHost, scopePort, scopeRealm ); + } + + /** + * @return the port + */ + public String getPort() + { + return port; + } + + /** + * @param port the port to set + */ + public void setPort( String port ) + { + this.port = port; + } + + /** + * Given a HttpHost, return scope with overrides from appropriate basicAuth + * configuration. + * <p> + * Note: Protocol is ignored. AuthScope impl ignores it as well, but if that + * changed, there could be a problem. + * </p> + * + * @param targetHost + * @return + */ + public AuthScope getScope( HttpHost targetHost ) + { + return getScope( targetHost.getHostName(), targetHost.getPort() ); + } +} http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/ConfigurationUtils.java ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/ConfigurationUtils.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/ConfigurationUtils.java new file mode 100755 index 0000000..3c82851 --- /dev/null +++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/ConfigurationUtils.java @@ -0,0 +1,233 @@ +package org.apache.maven.wagon.shared.http; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.message.BasicHeader; +import org.apache.maven.wagon.Wagon; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Configuration helper class + */ +public class ConfigurationUtils +{ + + private static final String SO_TIMEOUT = "http.socket.timeout"; + private static final String STALE_CONNECTION_CHECK = "http.connection.stalecheck"; + private static final String CONNECTION_TIMEOUT = "http.connection.timeout"; + private static final String USE_EXPECT_CONTINUE = "http.protocol.expect-continue"; + private static final String DEFAULT_PROXY = "http.route.default-proxy"; + private static final String LOCAL_ADDRESS = "http.route.local-address"; + private static final String PROXY_AUTH_PREF = "http.auth.proxy-scheme-pref"; + private static final String TARGET_AUTH_PREF = "http.auth.target-scheme-pref"; + private static final String HANDLE_AUTHENTICATION = "http.protocol.handle-authentication"; + private static final String ALLOW_CIRCULAR_REDIRECTS = "http.protocol.allow-circular-redirects"; + private static final String CONN_MANAGER_TIMEOUT = "http.conn-manager.timeout"; + private static final String COOKIE_POLICY = "http.protocol.cookie-policy"; + private static final String MAX_REDIRECTS = "http.protocol.max-redirects"; + private static final String HANDLE_REDIRECTS = "http.protocol.handle-redirects"; + private static final String REJECT_RELATIVE_REDIRECT = "http.protocol.reject-relative-redirect"; + + private static final String COERCE_PATTERN = "%(\\w+),(.+)"; + + public static void copyConfig( HttpMethodConfiguration config, RequestConfig.Builder builder ) + { + if ( config.getConnectionTimeout() > 0 ) + { + builder.setConnectTimeout( config.getConnectionTimeout() ); + } + if ( config.getReadTimeout() > 0 ) + { + builder.setSocketTimeout( config.getReadTimeout() ); + } + Properties params = config.getParams(); + if ( params != null ) + { + + Pattern coercePattern = Pattern.compile( COERCE_PATTERN ); + for ( Map.Entry entry : params.entrySet() ) + { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + Matcher matcher = coercePattern.matcher( value ); + if ( matcher.matches() ) + { + value = matcher.group( 2 ); + } + + if ( key.equals( SO_TIMEOUT ) ) + { + builder.setSocketTimeout( Integer.parseInt( value ) ); + } + else if ( key.equals( STALE_CONNECTION_CHECK ) ) + { + builder.setStaleConnectionCheckEnabled( Boolean.valueOf( value ) ); + } + else if ( key.equals( CONNECTION_TIMEOUT ) ) + { + builder.setConnectTimeout( Integer.parseInt( value ) ); + } + else if ( key.equals( USE_EXPECT_CONTINUE ) ) + { + builder.setExpectContinueEnabled( Boolean.valueOf( value ) ); + } + else if ( key.equals( DEFAULT_PROXY ) ) + { + builder.setProxy( new HttpHost( value ) ); + } + else if ( key.equals( LOCAL_ADDRESS ) ) + { + try + { + builder.setLocalAddress( InetAddress.getByName( value ) ); + } + catch ( UnknownHostException ignore ) + { + // ignore + } + } + else if ( key.equals( PROXY_AUTH_PREF ) ) + { + builder.setProxyPreferredAuthSchemes( Arrays.asList( value.split( "," ) ) ); + } + else if ( key.equals( TARGET_AUTH_PREF ) ) + { + builder.setTargetPreferredAuthSchemes( Arrays.asList( value.split( "," ) ) ); + } + else if ( key.equals( HANDLE_AUTHENTICATION ) ) + { + builder.setAuthenticationEnabled( Boolean.valueOf( value ) ); + } + else if ( key.equals( ALLOW_CIRCULAR_REDIRECTS ) ) + { + builder.setCircularRedirectsAllowed( Boolean.valueOf( value ) ); + } + else if ( key.equals( CONN_MANAGER_TIMEOUT ) ) + { + builder.setConnectionRequestTimeout( Integer.parseInt( value ) ); + } + else if ( key.equals( COOKIE_POLICY ) ) + { + builder.setCookieSpec( value ); + } + else if ( key.equals( MAX_REDIRECTS ) ) + { + builder.setMaxRedirects( Integer.parseInt( value ) ); + } + else if ( key.equals( HANDLE_REDIRECTS ) ) + { + builder.setRedirectsEnabled( Boolean.valueOf( value ) ); + } + else if ( key.equals( REJECT_RELATIVE_REDIRECT ) ) + { + builder.setRelativeRedirectsAllowed( !Boolean.valueOf( value ) ); + } + } + } + } + + public static Header[] asRequestHeaders( HttpMethodConfiguration config ) + { + Properties headers = config.getHeaders(); + if ( headers == null ) + { + return new Header[0]; + } + + Header[] result = new Header[headers.size()]; + + int index = 0; + for ( Map.Entry entry : headers.entrySet() ) + { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + + Header header = new BasicHeader( key, value ); + result[index++] = header; + } + + return result; + } + + public static HttpMethodConfiguration merge( HttpMethodConfiguration defaults, HttpMethodConfiguration base, + HttpMethodConfiguration local ) + { + HttpMethodConfiguration result = merge( defaults, base ); + return merge( result, local ); + } + + public static HttpMethodConfiguration merge( HttpMethodConfiguration base, HttpMethodConfiguration local ) + { + if ( base == null && local == null ) + { + return null; + } + else if ( base == null ) + { + return local; + } + else if ( local == null ) + { + return base; + } + else + { + HttpMethodConfiguration result = base.copy(); + + if ( local.getConnectionTimeout() != Wagon.DEFAULT_CONNECTION_TIMEOUT ) + { + result.setConnectionTimeout( local.getConnectionTimeout() ); + } + + if ( local.getReadTimeout() != Wagon.DEFAULT_READ_TIMEOUT ) + { + result.setReadTimeout( local.getReadTimeout() ); + } + + if ( local.getHeaders() != null ) + { + result.getHeaders().putAll( local.getHeaders() ); + } + + if ( local.getParams() != null ) + { + result.getParams().putAll( local.getParams() ); + } + + if ( local.getUseDefaultHeaders() != null ) + { + result.setUseDefaultHeaders( local.isUseDefaultHeaders() ); + } + + return result; + } + } + +} http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpConfiguration.java ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpConfiguration.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpConfiguration.java new file mode 100644 index 0000000..34fc61c --- /dev/null +++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpConfiguration.java @@ -0,0 +1,106 @@ +package org.apache.maven.wagon.shared.http; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; + +/** + * + */ +public class HttpConfiguration +{ + + private static final HttpMethodConfiguration DEFAULT_PUT = + new HttpMethodConfiguration().addParam( "http.protocol.expect-continue", "%b,true" ); + + private HttpMethodConfiguration all; + + private HttpMethodConfiguration get; + + private HttpMethodConfiguration put; + + private HttpMethodConfiguration head; + + public HttpMethodConfiguration getAll() + { + return all; + } + + public HttpConfiguration setAll( HttpMethodConfiguration all ) + { + this.all = all; + return this; + } + + public HttpMethodConfiguration getGet() + { + return get; + } + + public HttpConfiguration setGet( HttpMethodConfiguration get ) + { + this.get = get; + return this; + } + + public HttpMethodConfiguration getPut() + { + return put; + } + + public HttpConfiguration setPut( HttpMethodConfiguration put ) + { + this.put = put; + return this; + } + + public HttpMethodConfiguration getHead() + { + return head; + } + + public HttpConfiguration setHead( HttpMethodConfiguration head ) + { + this.head = head; + return this; + } + + public HttpMethodConfiguration getMethodConfiguration( HttpUriRequest method ) + { + if ( method instanceof HttpGet ) + { + return ConfigurationUtils.merge( all, get ); + } + else if ( method instanceof HttpPut ) + { + return ConfigurationUtils.merge( DEFAULT_PUT, all, put ); + } + else if ( method instanceof HttpHead ) + { + return ConfigurationUtils.merge( all, head ); + } + + return all; + } + +} http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpMethodConfiguration.java ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpMethodConfiguration.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpMethodConfiguration.java new file mode 100755 index 0000000..dcc1bd5 --- /dev/null +++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/HttpMethodConfiguration.java @@ -0,0 +1,174 @@ +package org.apache.maven.wagon.shared.http; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; +import org.apache.maven.wagon.Wagon; + +import java.util.Map; +import java.util.Properties; + +/** + * + */ +public class HttpMethodConfiguration +{ + + private Boolean useDefaultHeaders; + + private Properties headers = new Properties(); + + private Properties params = new Properties(); + + private int connectionTimeout = Wagon.DEFAULT_CONNECTION_TIMEOUT; + + private int readTimeout = + Integer.parseInt( System.getProperty( "maven.wagon.rto", Integer.toString( Wagon.DEFAULT_READ_TIMEOUT ) ) ); + + private boolean usePreemptive = false; + + public boolean isUseDefaultHeaders() + { + return useDefaultHeaders == null || useDefaultHeaders.booleanValue(); + } + + public HttpMethodConfiguration setUseDefaultHeaders( boolean useDefaultHeaders ) + { + this.useDefaultHeaders = Boolean.valueOf( useDefaultHeaders ); + return this; + } + + public Boolean getUseDefaultHeaders() + { + return useDefaultHeaders; + } + + public HttpMethodConfiguration addHeader( String header, String value ) + { + headers.setProperty( header, value ); + return this; + } + + public Properties getHeaders() + { + return headers; + } + + public HttpMethodConfiguration setHeaders( Properties headers ) + { + this.headers = headers; + return this; + } + + public HttpMethodConfiguration addParam( String param, String value ) + { + params.setProperty( param, value ); + return this; + } + + public Properties getParams() + { + return params; + } + + public HttpMethodConfiguration setParams( Properties params ) + { + this.params = params; + return this; + } + + public int getConnectionTimeout() + { + return connectionTimeout; + } + + public HttpMethodConfiguration setConnectionTimeout( int connectionTimeout ) + { + this.connectionTimeout = connectionTimeout; + return this; + } + + public int getReadTimeout() + { + return readTimeout; + } + + public HttpMethodConfiguration setReadTimeout( int readTimeout ) + { + this.readTimeout = readTimeout; + return this; + } + + public boolean isUsePreemptive() + { + return usePreemptive; + } + + public HttpMethodConfiguration setUsePreemptive( boolean usePreemptive ) + { + this.usePreemptive = usePreemptive; + return this; + } + + public Header[] asRequestHeaders() + { + if ( headers == null ) + { + return new Header[0]; + } + + Header[] result = new Header[headers.size()]; + + int index = 0; + for ( Map.Entry entry : headers.entrySet() ) + { + String key = (String) entry.getKey(); + String value = (String) entry.getValue(); + + Header header = new BasicHeader( key, value ); + result[index++] = header; + } + + return result; + } + + HttpMethodConfiguration copy() + { + HttpMethodConfiguration copy = new HttpMethodConfiguration(); + + copy.setConnectionTimeout( getConnectionTimeout() ); + copy.setReadTimeout( getReadTimeout() ); + if ( getHeaders() != null ) + { + copy.setHeaders( getHeaders() ); + } + + if ( getParams() != null ) + { + copy.setParams( getParams() ); + } + + copy.setUseDefaultHeaders( isUseDefaultHeaders() ); + + return copy; + } + +} http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/RelaxedTrustStrategy.java ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/RelaxedTrustStrategy.java b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/RelaxedTrustStrategy.java new file mode 100644 index 0000000..ca9bc9a --- /dev/null +++ b/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/RelaxedTrustStrategy.java @@ -0,0 +1,76 @@ +package org.apache.maven.wagon.shared.http; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import org.apache.http.conn.ssl.TrustStrategy; + +import java.security.cert.CertificateException; +import java.security.cert.CertificateExpiredException; +import java.security.cert.CertificateNotYetValidException; +import java.security.cert.X509Certificate; + +/** + * Relaxed X509 certificate trust manager: can ignore invalid certificate date. + * + * @author Olivier Lamy + * @since 2.0 + */ +public class RelaxedTrustStrategy + implements TrustStrategy +{ + private final boolean ignoreSSLValidityDates; + + public RelaxedTrustStrategy( boolean ignoreSSLValidityDates ) + { + this.ignoreSSLValidityDates = ignoreSSLValidityDates; + } + + public boolean isTrusted( X509Certificate[] certificates, String authType ) + throws CertificateException + { + if ( ( certificates != null ) && ( certificates.length == 1 ) ) + { + try + { + certificates[0].checkValidity(); + } + catch ( CertificateExpiredException e ) + { + if ( !ignoreSSLValidityDates ) + { + throw e; + } + } + catch ( CertificateNotYetValidException e ) + { + if ( !ignoreSSLValidityDates ) + { + throw e; + } + } + return true; + } + else + { + return false; + } + } + +} http://git-wip-us.apache.org/repos/asf/maven-wagon/blob/2a129274/wagon-providers/wagon-http/pom.xml ---------------------------------------------------------------------- diff --git a/wagon-providers/wagon-http/pom.xml b/wagon-providers/wagon-http/pom.xml index ccc498c..bc68268 100644 --- a/wagon-providers/wagon-http/pom.xml +++ b/wagon-providers/wagon-http/pom.xml @@ -51,6 +51,10 @@ under the License. </exclusions> </dependency> <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpcore</artifactId> + </dependency> + <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <scope>test</scope> @@ -69,10 +73,6 @@ under the License. <artifactId>plexus-utils</artifactId> </dependency> <dependency> - <groupId>org.apache.httpcomponents</groupId> - <artifactId>httpcore</artifactId> - </dependency> - <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope>