diff --git a/ivy/src/main/java/internal/librarymanagement/JavaNetAuthenticator.java b/ivy/src/main/java/internal/librarymanagement/JavaNetAuthenticator.java new file mode 100644 index 000000000..10a0e6285 --- /dev/null +++ b/ivy/src/main/java/internal/librarymanagement/JavaNetAuthenticator.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 Square, Inc. + * + * Licensed 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. + */ +package sbt.internal.librarymanagement; + +import java.io.IOException; +import java.net.Authenticator.RequestorType; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.util.List; +import okhttp3.Authenticator; +import okhttp3.Route; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.HttpUrl; +import okhttp3.Challenge; +import okhttp3.Credentials; + +/** + * Adapts {@link java.net.Authenticator} to {@link Authenticator}. Configure OkHttp to use {@link + * java.net.Authenticator} with {@link OkHttpClient.Builder#authenticator} or {@link + * OkHttpClient.Builder#proxyAuthenticator(Authenticator)}. + */ +public final class JavaNetAuthenticator implements Authenticator { + @Override public Request authenticate(Route route, Response response) throws IOException { + List challenges = response.challenges(); + Request request = response.request(); + HttpUrl url = request.url(); + boolean proxyAuthorization = response.code() == 407; + Proxy proxy = null; + if (route != null) { + proxy = route.proxy(); + } + + for (int i = 0, size = challenges.size(); i < size; i++) { + Challenge challenge = challenges.get(i); + if (!"Basic".equalsIgnoreCase(challenge.scheme())) continue; + + PasswordAuthentication auth; + if (proxyAuthorization) { + InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address(); + auth = java.net.Authenticator.requestPasswordAuthentication( + proxyAddress.getHostName(), getConnectToInetAddress(proxy, url), proxyAddress.getPort(), + url.scheme(), challenge.realm(), challenge.scheme(), url.url(), + RequestorType.PROXY); + } else { + auth = java.net.Authenticator.requestPasswordAuthentication( + url.host(), getConnectToInetAddress(proxy, url), url.port(), url.scheme(), + challenge.realm(), challenge.scheme(), url.url(), RequestorType.SERVER); + } + + if (auth != null) { + String credential = Credentials.basic(auth.getUserName(), new String(auth.getPassword())); + return request.newBuilder() + .header(proxyAuthorization ? "Proxy-Authorization" : "Authorization", credential) + .build(); + } + } + + return null; // No challenges were satisfied! + } + + private InetAddress getConnectToInetAddress(Proxy proxy, HttpUrl url) throws IOException { + return (proxy != null && proxy.type() != Proxy.Type.DIRECT) + ? ((InetSocketAddress) proxy.address()).getAddress() + : InetAddress.getByName(url.host()); + } +} diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/GigahorseUrlHandler.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/GigahorseUrlHandler.scala index 38f520867..8d26f38d5 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/GigahorseUrlHandler.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/GigahorseUrlHandler.scala @@ -9,7 +9,7 @@ import scala.util.control.NonFatal import okhttp3.{ MediaType, OkUrlFactory, Request, RequestBody } import okhttp3.internal.http.HttpDate -import okhttp3._ +import okhttp3.{ JavaNetAuthenticator => _, _ } import okio._ import org.apache.ivy.util.{ CopyProgressEvent, CopyProgressListener, Message } @@ -198,7 +198,7 @@ class GigahorseUrlHandler extends AbstractURLHandler { object GigahorseUrlHandler { import gigahorse.HttpClient - import okhttp3.{ OkHttpClient, JavaNetAuthenticator } + import okhttp3.OkHttpClient import sbt.librarymanagement.Http // This is requires to access the constructor of URLInfo. @@ -220,7 +220,7 @@ object GigahorseUrlHandler { http .underlying[OkHttpClient] .newBuilder() - .authenticator(new JavaNetAuthenticator) + .authenticator(new sbt.internal.librarymanagement.JavaNetAuthenticator) .followRedirects(true) .followSslRedirects(true) .build diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala index f461d0cdf..235f280d5 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/ParallelResolveEngine.scala @@ -1,5 +1,7 @@ package sbt.internal.librarymanagement.ivyint +import java.util.concurrent.Executors + import org.apache.ivy.core.event.EventManager import org.apache.ivy.core.event.download.PrepareDownloadEvent import org.apache.ivy.core.module.descriptor.Artifact @@ -9,12 +11,18 @@ import org.apache.ivy.core.sort.SortEngine import org.apache.ivy.util.Message import org.apache.ivy.util.filter.Filter -import scala.collection.parallel.mutable.ParArray +import scala.concurrent.duration.Duration +import scala.concurrent.{ Await, ExecutionContext, Future } private[ivyint] case class DownloadResult(dep: IvyNode, report: DownloadReport, totalSizeDownloaded: Long) +object ParallelResolveEngine { + private val resolveExecutionContext = + ExecutionContext.fromExecutor(Executors.newCachedThreadPool()) +} + /** Define an ivy [[ResolveEngine]] that resolves dependencies in parallel. */ private[sbt] class ParallelResolveEngine(settings: ResolveEngineSettings, eventManager: EventManager, @@ -24,30 +32,27 @@ private[sbt] class ParallelResolveEngine(settings: ResolveEngineSettings, override def downloadArtifacts(report: ResolveReport, artifactFilter: Filter, options: DownloadOptions): Unit = { - + import scala.collection.JavaConverters._ val start = System.currentTimeMillis - val dependencies0 = report.getDependencies - val dependencies = dependencies0 - .toArray(new Array[IvyNode](dependencies0.size)) - val artifacts = report.getArtifacts - .toArray(new Array[Artifact](report.getArtifacts.size)) - - getEventManager.fireIvyEvent(new PrepareDownloadEvent(artifacts)) - - // Farm out the dependencies for parallel download - val allDownloads = dependencies.par.flatMap { dep => - if (!(dep.isCompletelyEvicted || dep.hasProblem) && - dep.getModuleRevision != null) { - //don't block in global ec to avoid deadlocks - scala.concurrent.blocking { - ParArray(downloadNodeArtifacts(dep, artifactFilter, options)) - } - } else ParArray.empty[DownloadResult] + report.getArtifacts match { + case typed: java.util.List[Artifact @unchecked] => + new PrepareDownloadEvent(typed.asScala.toArray) } - - // Force parallel downloads and compute total downloaded size - val totalSize = allDownloads.toArray.foldLeft(0L) { - case (size, download) => + // Farm out the dependencies for parallel download + implicit val ec = ParallelResolveEngine.resolveExecutionContext + val allDownloadsFuture = Future.traverse(report.getDependencies.asScala) { + case dep: IvyNode => + Future { + if (!(dep.isCompletelyEvicted || dep.hasProblem) && + dep.getModuleRevision != null) { + Some(downloadNodeArtifacts(dep, artifactFilter, options)) + } else None + } + } + val allDownloads = Await.result(allDownloadsFuture, Duration.Inf) + //compute total downloaded size + val totalSize = allDownloads.foldLeft(0L) { + case (size, Some(download)) => val dependency = download.dep val moduleConfigurations = dependency.getRootModuleConfigurations moduleConfigurations.foreach { configuration => @@ -61,6 +66,7 @@ private[sbt] class ParallelResolveEngine(settings: ResolveEngineSettings, } size + download.totalSizeDownloaded + case (size, None) => size } report.setDownloadTime(System.currentTimeMillis() - start) diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala index c7c8904bf..517c26682 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala @@ -216,7 +216,7 @@ private[sbt] case class SbtChainResolver( val firstHit = sortedRevisions.reverse.headOption firstHit.map { hit => val (resolvedModule, resolver) = hit - + if (resolvedModule.getId.getRevision.contains("SNAPSHOT")) { Message.warn( diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f49220688..73b38a1df 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,7 +6,7 @@ object Dependencies { val scala211 = "2.11.12" val scala212 = "2.12.6" - private val ioVersion = "1.2.0-M1" + private val ioVersion = "1.2.0-M2" private val utilVersion = "1.2.0-M1" private val sbtIO = "org.scala-sbt" %% "io" % ioVersion