From e97eaa18b505b842cc804dafab1a11b06abe92a3 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 27 Dec 2017 01:14:01 +0100 Subject: [PATCH] Remove http-server module Now has its own repo at https://github.com/coursier/http-server Same Maven coordinates as before, `io.get-coursier:http-server_2.12` --- appveyor.yml | 1 - build.sbt | 20 -- .../main/scala-2.12/coursier/HttpServer.scala | 336 ------------------ project/Deps.scala | 3 - project/SharedVersions.scala | 1 - scripts/travis.sh | 2 - 6 files changed, 363 deletions(-) delete mode 100644 http-server/src/main/scala-2.12/coursier/HttpServer.scala diff --git a/appveyor.yml b/appveyor.yml index 5da959559..87b55dd9d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,7 +16,6 @@ install: - git submodule update --init --recursive build_script: - sbt ++2.11.11 clean compile coreJVM/publishLocal cli/publishLocal - - sbt ++2.12.4 http-server/publishLocal - sbt ++2.10.6 clean compile - sbt ++2.12.4 coreJVM/publishLocal cache/publishLocal extra/publishLocal sbt-shared/publishLocal # to make the scripted sbt 1.0 tests happy - sbt ++2.10.6 coreJVM/publishLocal cache/publishLocal extra/publishLocal sbt-shared/publishLocal # to make the scripted sbt 0.13 tests happy diff --git a/build.sbt b/build.sbt index 44ba28c1d..380599e1f 100644 --- a/build.sbt +++ b/build.sbt @@ -276,24 +276,6 @@ lazy val `sbt-launcher` = project } ) -lazy val `http-server` = project - .enablePlugins(PackPlugin) - .settings( - shared, - dontPublishIn("2.10", "2.11"), - libs ++= { - if (scalaBinaryVersion.value == "2.12") - Seq( - Deps.http4sBlazeServer, - Deps.http4sDsl, - Deps.slf4jNop, - Deps.caseApp12 - ) - else - Nil - } - ) - lazy val okhttp = project .dependsOn(cache) .settings( @@ -319,7 +301,6 @@ lazy val jvm = project `sbt-shading`, `sbt-launcher`, doc, - `http-server`, okhttp ) .settings( @@ -380,7 +361,6 @@ lazy val coursier = project `sbt-launcher`, web, doc, - `http-server`, okhttp ) .settings( diff --git a/http-server/src/main/scala-2.12/coursier/HttpServer.scala b/http-server/src/main/scala-2.12/coursier/HttpServer.scala deleted file mode 100644 index 4135997ff..000000000 --- a/http-server/src/main/scala-2.12/coursier/HttpServer.scala +++ /dev/null @@ -1,336 +0,0 @@ -package coursier - -import java.io.{File, FileOutputStream} -import java.net.NetworkInterface -import java.nio.channels.{FileLock, OverlappingFileLockException} - -import org.http4s._ -import org.http4s.dsl._ -import org.http4s.headers.{Authorization, `Content-Type`} -import org.http4s.server.blaze.BlazeBuilder -import org.http4s.server.{Server, ServerApp} - -import caseapp._ -import caseapp.core.WithHelp - -import scala.collection.JavaConverters._ -import scalaz.concurrent.Task - -final case class AuthOptions( - @ExtraName("u") - @ValueDescription("user") - user: String = "", - @ExtraName("P") - @ValueDescription("password") - password: String = "", - @ExtraName("r") - @ValueDescription("realm") - realm: String = "" -) { - def checks(): Unit = { - if (user.nonEmpty && password.isEmpty) - Console.err.println( - "Warning: authentication enabled but no password specified. " + - "Specify one with the --password or -P option." - ) - - if (password.nonEmpty && user.isEmpty) - Console.err.println( - "Warning: authentication enabled but no user specified. " + - "Specify one with the --user or -u option." - ) - - if ((user.nonEmpty || password.nonEmpty) && realm.isEmpty) - Console.err.println( - "Warning: authentication enabled but no realm specified. " + - "Specify one with the --realm or -r option." - ) - } -} - -final case class VerbosityOptions( - @ExtraName("v") - verbose: Int @@ Counter = Tag.of(0), - @ExtraName("q") - quiet: Boolean = false -) { - lazy val verbosityLevel = Tag.unwrap(verbose) - (if (quiet) 1 else 0) -} - -final case class HttpServerOptions( - @Recurse - auth: AuthOptions = AuthOptions(), - @Recurse - verbosity: VerbosityOptions = VerbosityOptions(), - @ExtraName("d") - @ValueDescription("served directory") - directory: String = ".", - @ExtraName("h") - @ValueDescription("host") - host: String = "0.0.0.0", - @ExtraName("p") - @ValueDescription("port") - port: Int = 8080, - @ExtraName("s") - acceptPost: Boolean = false, - @ExtraName("t") - acceptPut: Boolean = false, - @ExtraName("w") - @HelpMessage("Accept write requests. Equivalent to -s -t") - acceptWrite: Boolean = false, - @ExtraName("l") - @HelpMessage("Generate content listing pages for directories") - listPages: Boolean = false -) - -object HttpServer { - - def write(baseDir: File, path: Seq[String], req: Request): Boolean = { - - val f = new File(baseDir, path.toList.mkString("/")) - f.getParentFile.mkdirs() - - var os: FileOutputStream = null - var lock: FileLock = null - try { - os = new FileOutputStream(f) - lock = - try os.getChannel.tryLock() - catch { - case _: OverlappingFileLockException => - null - } - - if (lock == null) - false - else { - req.body.runLog.unsafePerformSync.foreach { b => - b.copyToStream(os) - } - - true - } - } finally { - if (lock != null) - lock.release() - if (os != null) - os.close() - } - } - - def isDirectory(f: File): Task[Option[Boolean]] = - Task { - if (f.isDirectory) - Some(true) - else if (f.isFile) - Some(false) - else - None - } - - - - def directoryListingPage(dir: File, title: String): Task[String] = - Task { - val entries = dir - .listFiles() - .flatMap { f => - def name = f.getName - if (f.isDirectory) - Seq(name + "/") - else if (f.isFile) - Seq(name) - else - Nil - } - - // meh escaping - // TODO Use to scalatags to generate that - s""" - | - | - |$title - | - | - | - | - | - """.stripMargin - } - - - def unauthorized(realm: String) = Unauthorized(Challenge("Basic", realm)) - - def authenticated0(options: AuthOptions, verbosityLevel: Int)(service: HttpService): HttpService = - if (options.user.isEmpty && options.password.isEmpty) - service - else - HttpService { - case req => - def warn(msg: => String) = - if (verbosityLevel >= 1) - Console.err.println(s"${req.method.name} ${req.uri.path}: $msg") - - req.headers.get(Authorization) match { - case None => - warn("no authentication provided") - unauthorized(options.realm) - case Some(auth) => - auth.credentials match { - case basic: BasicCredentials => - if (basic.username == options.user && basic.password == options.password) - service.run(req) - else { - warn { - val msg = - if (basic.username == options.user) - "wrong password" - else - s"unrecognized user ${basic.username}" - - s"authentication failed ($msg)" - } - unauthorized(options.realm) - } - case _ => - warn("no basic credentials found") - unauthorized(options.realm) - } - } - } - - def authenticated(options: AuthOptions, verbosityLevel: Int)(pf: PartialFunction[Request, Task[Response]]): HttpService = - authenticated0(options, verbosityLevel)(HttpService(pf)) - - def putService(baseDir: File, auth: AuthOptions, verbosityLevel: Int) = - authenticated(auth, verbosityLevel) { - case req @ PUT -> path => - if (verbosityLevel >= 1) - Console.err.println(s"PUT $path") - - if (write(baseDir, path.toList, req)) - Ok() - else - Locked() - } - - def postService(baseDir: File, auth: AuthOptions, verbosityLevel: Int) = - authenticated(auth, verbosityLevel) { - case req @ POST -> path => - if (verbosityLevel >= 1) - Console.err.println(s"POST $path") - - if (write(baseDir, path.toList, req)) - Ok() - else - Locked() - } - - def getService(baseDir: File, auth: AuthOptions, verbosityLevel: Int, listPages: Boolean) = - authenticated(auth, verbosityLevel) { - case (method @ (GET | HEAD)) -> path => - if (verbosityLevel >= 1) - Console.err.println(s"${method.name} $path") - - val relPath = path.toList.mkString("/") - val f = new File(baseDir, relPath) - val resp = - for { - isDirOpt <- isDirectory(f) - resp <- isDirOpt match { - case Some(true) if listPages => - directoryListingPage(f, relPath).flatMap(page => - Ok(page).withContentType(Some(`Content-Type`(MediaType.`text/html`))) - ) - case Some(false) => Ok(f) - case _ => NotFound() - } - } yield resp - - method match { - case HEAD => - resp.map(_.copy(body = EmptyBody)) - case _ => - resp - } - } - - def server(options: HttpServerOptions): Task[Server] = { - - val baseDir = new File(if (options.directory.isEmpty) "." else options.directory) - - options.auth.checks() - - val verbosityLevel = options.verbosity.verbosityLevel - - val builder = { - var b = BlazeBuilder.bindHttp(options.port, options.host) - - if (options.acceptWrite || options.acceptPut) - b = b.mountService(putService(baseDir, options.auth, verbosityLevel)) - if (options.acceptWrite || options.acceptPost) - b = b.mountService(postService(baseDir, options.auth, verbosityLevel)) - - b = b.mountService(getService(baseDir, options.auth, verbosityLevel, options.listPages)) - - b - } - - if (verbosityLevel >= 0) { - Console.err.println(s"Listening on http://${options.host}:${options.port}") - - if (verbosityLevel >= 1 && options.host == "0.0.0.0") { - Console.err.println(s"Listening on addresses") - for (itf <- NetworkInterface.getNetworkInterfaces.asScala; addr <- itf.getInetAddresses.asScala) - Console.err.println(s" ${addr.getHostAddress} (${itf.getName})") - } - } - - builder.start - } - -} - -object HttpServerApp extends ServerApp { - - val app: CaseApp[HttpServerOptions] = - new CaseApp[HttpServerOptions] { - def run(options: HttpServerOptions, args: RemainingArgs) = - sys.error("unused") - } - - def server(args: List[String]): Task[Server] = - app.parser.withHelp.detailedParse(args) match { - case Left(err) => - app.error(err) - sys.error("unreached") // need to adjust return type of error method in CaseApp - - case Right((WithHelp(usage, help, t), remainingArgs, extraArgs)) => - - if (help) - app.helpAsked() - - if (usage) - app.usageAsked() - - t match { - case Left(err) => - app.error(err) - sys.error("unreached") - - case Right(opts) => - - val extraArgs0 = remainingArgs ++ extraArgs - - if (extraArgs0.nonEmpty) - Console.err.println( - s"Warning: ignoring extra arguments passed on the command-line (${extraArgs0.mkString(", ")})" - ) - - HttpServer.server(opts) - } - } - -} diff --git a/project/Deps.scala b/project/Deps.scala index b0a7134df..84d2612dc 100644 --- a/project/Deps.scala +++ b/project/Deps.scala @@ -12,9 +12,6 @@ object Deps { def scalazConcurrent = "org.scalaz" %% "scalaz-concurrent" % SharedVersions.scalaz def caseApp = "com.github.alexarchambault" %% "case-app" % "1.1.3" def caseApp12 = "com.github.alexarchambault" %% "case-app" % "1.2.0" - def http4sBlazeServer = "org.http4s" %% "http4s-blaze-server" % SharedVersions.http4s - def http4sDsl = "org.http4s" %% "http4s-dsl" % SharedVersions.http4s - def slf4jNop = "org.slf4j" % "slf4j-nop" % "1.7.25" def okhttpUrlConnection = "com.squareup.okhttp" % "okhttp-urlconnection" % "2.7.5" def sbtLauncherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0" def typesafeConfig = "com.typesafe" % "config" % "1.3.2" diff --git a/project/SharedVersions.scala b/project/SharedVersions.scala index a9e407d18..2a16a0be6 100644 --- a/project/SharedVersions.scala +++ b/project/SharedVersions.scala @@ -3,7 +3,6 @@ object SharedVersions { def asm = "5.2" def fastParse = "1.0.0" - def http4s = "0.15.16a" def proguard = "5.3.3" def scalaNative = "0.3.0-coursier-1" def scalaz = "7.2.16" diff --git a/scripts/travis.sh b/scripts/travis.sh index 1c5c9910f..8edf9f6cb 100755 --- a/scripts/travis.sh +++ b/scripts/travis.sh @@ -49,8 +49,6 @@ integrationTestsRequirements() { # Required for ~/.ivy2/local repo tests sbt ++2.11.11 coreJVM/publishLocal cli/publishLocal - sbt ++2.12.4 http-server/publishLocal - # Required for HTTP authentication tests launchTestRepo --port 8080 --list-pages