From ccb1afe90e938fc2b8619dd960a1df1937f212be Mon Sep 17 00:00:00 2001 From: Sam Guymer Date: Mon, 21 May 2018 20:54:10 +1000 Subject: Code review updates Start the test server before each backend test --- build.sbt | 36 +--- core/src/test/resources/binaryfile.jpg | Bin 0 -> 42010 bytes core/src/test/resources/textfile.txt | 100 +++++++++ .../com/softwaremill/sttp/testing/HttpTest.scala | 40 ++-- .../softwaremill/sttp/testing/TestHttpServer.scala | 236 +++++++++++++++++++++ .../sttp/testing/streaming/StreamingTest.scala | 9 +- docs/backends/custom.rst | 2 + project/PollingUtils.scala | 45 ---- project/plugins.sbt | 5 +- test-server/src/main/resources/binaryfile.jpg | Bin 42010 -> 0 bytes test-server/src/main/resources/textfile.txt | 100 --------- .../softwaremill/sttp/server/TestHttpServer.scala | 197 ----------------- 12 files changed, 368 insertions(+), 402 deletions(-) create mode 100644 core/src/test/resources/binaryfile.jpg create mode 100644 core/src/test/resources/textfile.txt create mode 100644 core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala delete mode 100644 project/PollingUtils.scala delete mode 100644 test-server/src/main/resources/binaryfile.jpg delete mode 100644 test-server/src/main/resources/textfile.txt delete mode 100644 test-server/src/main/scala/com/softwaremill/sttp/server/TestHttpServer.scala diff --git a/build.sbt b/build.sbt index 6bcaf1a..2acfbf6 100644 --- a/build.sbt +++ b/build.sbt @@ -34,9 +34,6 @@ val akkaStreams = "com.typesafe.akka" %% "akka-stream" % "2.5.12" val scalaTest = "org.scalatest" %% "scalatest" % "3.0.5" -val testServerPort = settingKey[Int]("Port to run the http test server on") -val startTestServer = taskKey[Unit]("Start a http server used by tests") - lazy val rootProject = (project in file(".")) .settings(commonSettings: _*) .settings(skip in publish := true, name := "sttp") @@ -57,18 +54,18 @@ lazy val rootProject = (project in file(".")) circe, json4s, braveBackend, - prometheusBackend, - testServer + prometheusBackend ) lazy val core: Project = (project in file("core")) .settings(commonSettings: _*) - .settings(testServerSettings: _*) .settings( name := "core", libraryDependencies ++= Seq( "com.github.pathikrit" %% "better-files" % "3.4.0" % "test", "org.scala-lang" % "scala-compiler" % scalaVersion.value % "test", + akkaHttp % "test", + akkaStreams % "test", scalaTest % "test" ), publishArtifact in Test := true // allow implementations outside of this repo @@ -106,7 +103,6 @@ lazy val scalaz: Project = (project in file("implementations/scalaz")) //-- akka lazy val akkaHttpBackend: Project = (project in file("akka-http-backend")) .settings(commonSettings: _*) - .settings(testServerSettings: _*) .settings( name := "akka-http-backend", libraryDependencies ++= Seq( @@ -122,7 +118,6 @@ lazy val akkaHttpBackend: Project = (project in file("akka-http-backend")) lazy val asyncHttpClientBackend: Project = { (project in file("async-http-client-backend")) .settings(commonSettings: _*) - .settings(testServerSettings: _*) .settings( name := "async-http-client-backend", libraryDependencies ++= Seq( @@ -135,7 +130,6 @@ lazy val asyncHttpClientBackend: Project = { def asyncHttpClientBackendProject(proj: String): Project = { Project(s"asyncHttpClientBackend${proj.capitalize}", file(s"async-http-client-backend/$proj")) .settings(commonSettings: _*) - .settings(testServerSettings: _*) .settings(name := s"async-http-client-backend-$proj") .dependsOn(asyncHttpClientBackend) } @@ -168,7 +162,6 @@ lazy val asyncHttpClientFs2Backend: Project = //-- okhttp lazy val okhttpBackend: Project = (project in file("okhttp-backend")) .settings(commonSettings: _*) - .settings(testServerSettings: _*) .settings( name := "okhttp-backend", libraryDependencies ++= Seq( @@ -180,7 +173,6 @@ lazy val okhttpBackend: Project = (project in file("okhttp-backend")) def okhttpBackendProject(proj: String): Project = { Project(s"okhttpBackend${proj.capitalize}", file(s"okhttp-backend/$proj")) .settings(commonSettings: _*) - .settings(testServerSettings: _*) .settings(name := s"okhttp-backend-$proj") .dependsOn(okhttpBackend) } @@ -238,25 +230,3 @@ lazy val prometheusBackend: Project = (project in file("metrics/prometheus-backe ) ) .dependsOn(core) - -// https://stackoverflow.com/questions/25766797/how-do-i-start-a-server-before-running-a-test-suite-in-sbt -lazy val testServer: Project = project - .in(file("test-server")) - .settings(commonSettings: _*) - .settings( - name := "test-server", - libraryDependencies ++= Seq(akkaHttp, akkaStreams), - mainClass in reStart := Some("com.softwaremill.sttp.server.TestHttpServer"), - reStartArgs := Seq(s"${testServerPort.value}"), - testServerPort := 51823, - startTestServer := (reStart in Test).toTask("").value - ) - -lazy val testServerSettings = Seq( - test in Test := (test in Test).dependsOn(startTestServer in testServer).value, - testOnly in Test := (testOnly in Test).dependsOn(startTestServer in testServer).evaluated, - testOptions in Test += Tests.Setup(() => { - val port = (testServerPort in testServer).value - PollingUtils.waitUntilServerAvailable(new URL(s"http://localhost:$port")) - }) -) diff --git a/core/src/test/resources/binaryfile.jpg b/core/src/test/resources/binaryfile.jpg new file mode 100644 index 0000000..b9f5c5a Binary files /dev/null and b/core/src/test/resources/binaryfile.jpg differ diff --git a/core/src/test/resources/textfile.txt b/core/src/test/resources/textfile.txt new file mode 100644 index 0000000..9904f90 --- /dev/null +++ b/core/src/test/resources/textfile.txt @@ -0,0 +1,100 @@ +- Lorem ipsum dolor sit amet +- Vivamus sem ipsum. +- Ut molestie. +- Donec. +- Fusce non porta. +- Nulla ac metus. Morbi mattis. +- Etiam varius. +- Nulla. +- Phasellus id mollis. +- Suspendisse at. +- Quisque nec leo velit. +- Fusce. +- Maecenas nec tristique senectus et. +- Integer vestibulum lorem fermentum. +- Vestibulum consectetuer dolor. +- Lorem ipsum in. +- Fusce condimentum auctor scelerisque, wisi. +- Quisque. +- Curae. +- Curae, Nullam. +- Curae, Integer. +- Vestibulum dignissim massa. Donec. +- Pellentesque sed sem. +- Vivamus est. +- Maecenas elit sed est. +- Quisque sed tellus. +- Cum sociis natoque penatibus et. +- Fusce aliquam. +- Donec. +- Sed elementum, sapien accumsan odio. +- Nam mattis, magna lectus, tincidunt. +- Pellentesque scelerisque a, sodales. +- Sed sed condimentum. +- Curae, In nonummy. Phasellus adipiscing. +- Vestibulum quis diam mollis. +- Sed eros. Duis ipsum. +- Aenean pellentesque at, mollis tempus. +- Cras ornare facilisis sodales. Aenean. +- Cum sociis natoque penatibus. +- Donec id nulla. +- Quisque ut sapien. +- Phasellus purus. Proin ultricies. +- Aliquam auctor neque. Nunc. +- Nam nunc fringilla non, vehicula. +- Morbi molestie, felis ut lobortis. +- Nulla quis. +- In. +- Phasellus laoreet urna. +- Lorem ipsum. +- Phasellus. +- Class aptent taciti sociosqu ad. +- Sed lacinia. +- Pellentesque dapibus diam. Duis. +- Suspendisse est. Curabitur. +- Fusce condimentum justo. +- Aenean congue quis, faucibus. +- Ut pharetra leo. Donec. +- Fusce. +- Donec porta. +- Pellentesque orci. +- Sed. +- Quisque rutrum, wisi vulputate wisi. +- Cum sociis. +- Cras. +- Sed eros. Curabitur. +- Proin in velit wisi, tempor. +- Quisque eu. +- Proin. +- Nam pellentesque sed, imperdiet aliquam. +- Mauris euismod. Sed euismod orci. +- Etiam. +- Donec. +- Fusce wisi a metus. Proin. +- Phasellus quis. +- Donec non imperdiet. +- Aenean vel bibendum a, laoreet. +- Fusce non enim. Phasellus vulputate. +- Donec urna elit, sit. +- Pellentesque habitant morbi. +- Nulla ante. Curabitur elit. Donec. +- Cum sociis natoque penatibus. +- Maecenas eget leo at. +- Cum sociis natoque penatibus et. +- Vivamus lacus. +- Integer. +- Curae. +- Maecenas rhoncus. Morbi. +- Aenean posuere. +- Duis. +- Suspendisse a odio fermentum libero. +- Nam enim. Fusce enim. In. +- Maecenas. +- Lorem ipsum primis. +- Curabitur ac turpis semper sed. +- Quisque condimentum. Donec sit. +- Integer convallis non, posuere. +- Etiam vulputate, odio. +- Proin id lorem. Donec quis. +- Curae, Sed nec augue. +- Aliquam ut turpis. \ No newline at end of file diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala b/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala index f1fa002..5598aa2 100644 --- a/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala +++ b/core/src/test/scala/com/softwaremill/sttp/testing/HttpTest.scala @@ -22,26 +22,23 @@ trait HttpTest[R[_]] with OptionValues with IntegrationPatience with BeforeAndAfterEach - with BeforeAndAfterAll { - - private val endpoint = "localhost:51823" + with BeforeAndAfterAll + with TestHttpServer { override def afterEach() { val file = File(outPath) if (file.exists) file.delete() } - private val textFile = - new java.io.File("test-server/src/main/resources/textfile.txt") - private val binaryFile = - new java.io.File("test-server/src/main/resources/binaryfile.jpg") + private val textFile = new java.io.File(getClass.getResource("/textfile.txt").getFile) + private val binaryFile = new java.io.File(getClass.getResource("/binaryfile.jpg").getFile) private val outPath = File.newTemporaryDirectory().path private val textWithSpecialCharacters = "Żółć!" implicit val backend: SttpBackend[R, Nothing] implicit val convertToFuture: ConvertToFuture[R] - private val postEcho = sttp.post(uri"$endpoint/echo") + private def postEcho = sttp.post(uri"$endpoint/echo") private val testBody = "this is the body" private val testBodyBytes = testBody.getBytes("UTF-8") private val expectedPostEchoResponse = "POST /echo this is the body" @@ -172,7 +169,7 @@ trait HttpTest[R[_]] } "headers" - { - val getHeaders = sttp.get(uri"$endpoint/set_headers") + def getHeaders = sttp.get(uri"$endpoint/set_headers") "read response headers" in { val response = getHeaders.response(sttpIgnore).send().force() @@ -187,7 +184,7 @@ trait HttpTest[R[_]] } "errors" - { - val getHeaders = sttp.post(uri"$endpoint/set_headers") + def getHeaders = sttp.post(uri"$endpoint/set_headers") "return 405 when method not allowed" in { val response = getHeaders.response(sttpIgnore).send().force() @@ -236,7 +233,7 @@ trait HttpTest[R[_]] } "auth" - { - val secureBasic = sttp.get(uri"$endpoint/secure_basic") + def secureBasic = sttp.get(uri"$endpoint/secure_basic") "return a 401 when authorization fails" in { val req = secureBasic @@ -254,7 +251,7 @@ trait HttpTest[R[_]] } "compression" - { - val compress = sttp.get(uri"$endpoint/compress") + def compress = sttp.get(uri"$endpoint/compress") val decompressedBody = "I'm compressed!" "decompress using the default accept encoding header" in { @@ -374,7 +371,7 @@ trait HttpTest[R[_]] } "multipart" - { - val mp = sttp.post(uri"$endpoint/multipart") + def mp = sttp.post(uri"$endpoint/multipart") "send a multipart message" in { val req = mp.multipartBody(multipart("p1", "v1"), multipart("p2", "v2")) @@ -400,11 +397,11 @@ trait HttpTest[R[_]] } "redirect" - { - val r1 = sttp.post(uri"$endpoint/redirect/r1") - val r2 = sttp.post(uri"$endpoint/redirect/r2") - val r3 = sttp.post(uri"$endpoint/redirect/r3") + def r1 = sttp.post(uri"$endpoint/redirect/r1") + def r2 = sttp.post(uri"$endpoint/redirect/r2") + def r3 = sttp.post(uri"$endpoint/redirect/r3") val r4response = "819" - val loop = sttp.post(uri"$endpoint/redirect/loop") + def loop = sttp.post(uri"$endpoint/redirect/loop") "not redirect when redirects shouldn't be followed (temporary)" in { val resp = r1.followRedirects(false).send().force() @@ -499,10 +496,11 @@ trait HttpTest[R[_]] } "empty response" - { - val postEmptyResponse = sttp - .post(uri"$endpoint/empty_unauthorized_response") - .body("{}") - .contentType("application/json") + def postEmptyResponse = + sttp + .post(uri"$endpoint/empty_unauthorized_response") + .body("{}") + .contentType("application/json") "parse an empty error response as empty string" in { val response = postEmptyResponse.send().force() diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala b/core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala new file mode 100644 index 0000000..11fc692 --- /dev/null +++ b/core/src/test/scala/com/softwaremill/sttp/testing/TestHttpServer.scala @@ -0,0 +1,236 @@ +package com.softwaremill.sttp.testing + +import akka.Done +import akka.actor.ActorSystem +import akka.http.scaladsl.Http +import akka.http.scaladsl.coding.{Deflate, Gzip, NoCoding} +import akka.http.scaladsl.model._ +import akka.http.scaladsl.model.headers.CacheDirectives._ +import akka.http.scaladsl.model.headers._ +import akka.http.scaladsl.server.Directives.{entity, path, _} +import akka.http.scaladsl.server.Route +import akka.http.scaladsl.server.directives.Credentials +import akka.stream.ActorMaterializer +import akka.util.ByteString +import scala.concurrent.duration._ +import scala.concurrent.{Await, Future} + +import org.scalatest.BeforeAndAfterAll +import org.scalatest.Suite + +trait TestHttpServer extends BeforeAndAfterAll { this: Suite => + + private val server = new HttpServer(0) + protected var endpoint = "localhost:51823" + + override protected def beforeAll(): Unit = { + import scala.concurrent.ExecutionContext.Implicits.global + + super.beforeAll() + Await.result( + server.start().map { binding => + endpoint = s"localhost:${binding.localAddress.getPort}" + }, + 10.seconds + ) + } + + override protected def afterAll(): Unit = { + server.close() + super.afterAll() + } + +} + +object HttpServer { + + def main(args: Array[String]): Unit = { + val port = args.headOption.map(_.toInt).getOrElse(51823) + + Await.result(new HttpServer(port).start(), 10.seconds) + } +} + +private class HttpServer(port: Int) extends AutoCloseable { + + import scala.concurrent.ExecutionContext.Implicits.global + + private var server: Option[Future[Http.ServerBinding]] = None + + private implicit val actorSystem: ActorSystem = ActorSystem("sttp-test-server") + private implicit val materializer: ActorMaterializer = ActorMaterializer() + + private def paramsToString(m: Map[String, String]): String = + m.toList.sortBy(_._1).map(p => s"${p._1}=${p._2}").mkString(" ") + + private val textFile = new java.io.File(getClass.getResource("/textfile.txt").getFile) + private val binaryFile = new java.io.File(getClass.getResource("/binaryfile.jpg").getFile) + private val textWithSpecialCharacters = "Żółć!" + + val serverRoutes: Route = + pathPrefix("echo") { + pathPrefix("form_params") { + formFieldMap { params => + path("as_string") { + complete(paramsToString(params)) + } ~ + path("as_params") { + complete(FormData(params)) + } + } + } ~ get { + parameterMap { params => + complete(List("GET", "/echo", paramsToString(params)) + .filter(_.nonEmpty) + .mkString(" ")) + } + } ~ + post { + parameterMap { params => + entity(as[String]) { body: String => + complete(List("POST", "/echo", paramsToString(params), body) + .filter(_.nonEmpty) + .mkString(" ")) + } + } + } + } ~ pathPrefix("streaming") { + path("echo") { + post { + parameterMap { _ => + entity(as[String]) { body: String => + complete(body) + } + } + } + } + } ~ path("set_headers") { + get { + respondWithHeader(`Cache-Control`(`max-age`(1000L))) { + respondWithHeader(`Cache-Control`(`no-cache`)) { + complete("ok") + } + } + } + } ~ pathPrefix("set_cookies") { + path("with_expires") { + setCookie(HttpCookie("c", "v", expires = Some(DateTime(1997, 12, 8, 12, 49, 12)))) { + complete("ok") + } + } ~ get { + setCookie( + HttpCookie( + "cookie1", + "value1", + secure = true, + httpOnly = true, + maxAge = Some(123L) + ) + ) { + setCookie(HttpCookie("cookie2", "value2")) { + setCookie( + HttpCookie( + "cookie3", + "", + domain = Some("xyz"), + path = Some("a/b/c") + ) + ) { + complete("ok") + } + } + } + } + } ~ path("secure_basic") { + authenticateBasic("test realm", { + case c @ Credentials.Provided(un) if un == "adam" && c.verify("1234") => + Some(un) + case _ => None + }) { userName => + complete(s"Hello, $userName!") + } + } ~ path("compress") { + encodeResponseWith(Gzip, Deflate, NoCoding) { + complete("I'm compressed!") + } + } ~ pathPrefix("download") { + path("binary") { + getFromFile(binaryFile) + } ~ path("text") { + getFromFile(textFile) + } + } ~ pathPrefix("multipart") { + entity(as[akka.http.scaladsl.model.Multipart.FormData]) { fd => + complete { + fd.parts + .mapAsync(1) { p => + val fv = p.entity.dataBytes.runFold(ByteString())(_ ++ _) + fv.map(_.utf8String) + .map(v => p.name + "=" + v + p.filename.fold("")(fn => s" ($fn)")) + } + .runFold(Vector.empty[String])(_ :+ _) + .map(v => v.mkString(", ")) + } + } + } ~ pathPrefix("redirect") { + path("r1") { + redirect("/redirect/r2", StatusCodes.TemporaryRedirect) + } ~ + path("r2") { + redirect("/redirect/r3", StatusCodes.PermanentRedirect) + } ~ + path("r3") { + redirect("/redirect/r4", StatusCodes.Found) + } ~ + path("r4") { + complete("819") + } ~ + path("loop") { + redirect("/redirect/loop", StatusCodes.Found) + } + } ~ pathPrefix("timeout") { + complete { + akka.pattern.after(1.second, using = actorSystem.scheduler)( + Future.successful("Done") + ) + } + } ~ path("empty_unauthorized_response") { + post { + import akka.http.scaladsl.model._ + complete( + HttpResponse( + status = StatusCodes.Unauthorized, + headers = Nil, + entity = HttpEntity.Empty, + protocol = HttpProtocols.`HTTP/1.1` + )) + } + } ~ path("respond_with_iso_8859_2") { + get { ctx => + val entity = + HttpEntity(MediaTypes.`text/plain`.withCharset(HttpCharset.custom("ISO-8859-2")), textWithSpecialCharacters) + ctx.complete(HttpResponse(200, entity = entity)) + } + } + + def start(): Future[Http.ServerBinding] = { + unbindServer().flatMap { _ => + val server = Http().bindAndHandle(serverRoutes, "localhost", port) + this.server = Some(server) + server + } + } + + def close(): Unit = { + val unbind = unbindServer() + unbind.onComplete(_ => actorSystem.terminate()) + Await.result( + unbind, + 10.seconds + ) + } + + private def unbindServer(): Future[Done] = { + server.map(_.flatMap(_.unbind())).getOrElse(Future.successful(Done)) + } +} diff --git a/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala b/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala index 27a6eda..40aaf82 100644 --- a/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala +++ b/core/src/test/scala/com/softwaremill/sttp/testing/streaming/StreamingTest.scala @@ -6,10 +6,15 @@ import org.scalatest.{AsyncFreeSpec, BeforeAndAfterAll, Matchers} import scala.language.higherKinds import com.softwaremill.sttp.testing.ConvertToFuture +import com.softwaremill.sttp.testing.TestHttpServer -trait StreamingTest[R[_], S] extends AsyncFreeSpec with Matchers with BeforeAndAfterAll with ForceWrapped { +trait StreamingTest[R[_], S] + extends AsyncFreeSpec + with Matchers + with ForceWrapped + with BeforeAndAfterAll + with TestHttpServer { - private val endpoint = "localhost:51823" private val body = "streaming test" implicit def backend: SttpBackend[R, S] diff --git a/docs/backends/custom.rst b/docs/backends/custom.rst index 86bfb9d..c36b1d2 100644 --- a/docs/backends/custom.rst +++ b/docs/backends/custom.rst @@ -146,6 +146,8 @@ Implementing a new backend is made easy as the tests are published in the ``core "com.softwaremill.sttp" %% "core" % sttpVersion % "test" classifier "tests", "com.github.pathikrit" %% "better-files" % "3.4.0" % "test", + "com.typesafe.akka" %% "akka-http" % "10.1.1" % "test", + "com.typesafe.akka" %% "akka-stream" % "2.5.12" % "test", "org.scalatest" %% "scalatest" % "3.0.5" % "test" Implement your backend and extend the ``HttpTest`` class:: diff --git a/project/PollingUtils.scala b/project/PollingUtils.scala deleted file mode 100644 index 34b92ac..0000000 --- a/project/PollingUtils.scala +++ /dev/null @@ -1,45 +0,0 @@ -import java.io.FileNotFoundException -import java.net.{ConnectException, URL} - -import scala.concurrent.TimeoutException -import scala.concurrent.duration._ - -object PollingUtils { - - def waitUntilServerAvailable(url: URL): Unit = { - val connected = poll(5.seconds, 250.milliseconds)({ - urlConnectionAvailable(url) - }) - if (!connected) { - throw new TimeoutException(s"Failed to connect to $url") - } - } - - def poll(timeout: FiniteDuration, interval: FiniteDuration)(poll: => Boolean): Boolean = { - val start = System.nanoTime() - - def go(): Boolean = { - if (poll) { - true - } else if ((System.nanoTime() - start) > timeout.toNanos) { - false - } else { - Thread.sleep(interval.toMillis) - go() - } - } - go() - } - - def urlConnectionAvailable(url: URL): Boolean = { - try { - url.openConnection() - .getInputStream - .close() - true - } catch { - case _: ConnectException => false - case _: FileNotFoundException => true // on 404 - } - } -} diff --git a/project/plugins.sbt b/project/plugins.sbt index b334460..fa55342 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,4 @@ -// using '-coursier' because of https://github.com/lucidsoftware/neo-sbt-scalafmt/issues/64 -addSbtPlugin("com.lucidchart" % "sbt-scalafmt-coursier" % "1.15") +addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.15") addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.0") @@ -8,5 +7,3 @@ addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.7") addSbtPlugin("com.updateimpact" % "updateimpact-sbt-plugin" % "2.1.3") - -addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1") diff --git a/test-server/src/main/resources/binaryfile.jpg b/test-server/src/main/resources/binaryfile.jpg deleted file mode 100644 index b9f5c5a..0000000 Binary files a/test-server/src/main/resources/binaryfile.jpg and /dev/null differ diff --git a/test-server/src/main/resources/textfile.txt b/test-server/src/main/resources/textfile.txt deleted file mode 100644 index 9904f90..0000000 --- a/test-server/src/main/resources/textfile.txt +++ /dev/null @@ -1,100 +0,0 @@ -- Lorem ipsum dolor sit amet -- Vivamus sem ipsum. -- Ut molestie. -- Donec. -- Fusce non porta. -- Nulla ac metus. Morbi mattis. -- Etiam varius. -- Nulla. -- Phasellus id mollis. -- Suspendisse at. -- Quisque nec leo velit. -- Fusce. -- Maecenas nec tristique senectus et. -- Integer vestibulum lorem fermentum. -- Vestibulum consectetuer dolor. -- Lorem ipsum in. -- Fusce condimentum auctor scelerisque, wisi. -- Quisque. -- Curae. -- Curae, Nullam. -- Curae, Integer. -- Vestibulum dignissim massa. Donec. -- Pellentesque sed sem. -- Vivamus est. -- Maecenas elit sed est. -- Quisque sed tellus. -- Cum sociis natoque penatibus et. -- Fusce aliquam. -- Donec. -- Sed elementum, sapien accumsan odio. -- Nam mattis, magna lectus, tincidunt. -- Pellentesque scelerisque a, sodales. -- Sed sed condimentum. -- Curae, In nonummy. Phasellus adipiscing. -- Vestibulum quis diam mollis. -- Sed eros. Duis ipsum. -- Aenean pellentesque at, mollis tempus. -- Cras ornare facilisis sodales. Aenean. -- Cum sociis natoque penatibus. -- Donec id nulla. -- Quisque ut sapien. -- Phasellus purus. Proin ultricies. -- Aliquam auctor neque. Nunc. -- Nam nunc fringilla non, vehicula. -- Morbi molestie, felis ut lobortis. -- Nulla quis. -- In. -- Phasellus laoreet urna. -- Lorem ipsum. -- Phasellus. -- Class aptent taciti sociosqu ad. -- Sed lacinia. -- Pellentesque dapibus diam. Duis. -- Suspendisse est. Curabitur. -- Fusce condimentum justo. -- Aenean congue quis, faucibus. -- Ut pharetra leo. Donec. -- Fusce. -- Donec porta. -- Pellentesque orci. -- Sed. -- Quisque rutrum, wisi vulputate wisi. -- Cum sociis. -- Cras. -- Sed eros. Curabitur. -- Proin in velit wisi, tempor. -- Quisque eu. -- Proin. -- Nam pellentesque sed, imperdiet aliquam. -- Mauris euismod. Sed euismod orci. -- Etiam. -- Donec. -- Fusce wisi a metus. Proin. -- Phasellus quis. -- Donec non imperdiet. -- Aenean vel bibendum a, laoreet. -- Fusce non enim. Phasellus vulputate. -- Donec urna elit, sit. -- Pellentesque habitant morbi. -- Nulla ante. Curabitur elit. Donec. -- Cum sociis natoque penatibus. -- Maecenas eget leo at. -- Cum sociis natoque penatibus et. -- Vivamus lacus. -- Integer. -- Curae. -- Maecenas rhoncus. Morbi. -- Aenean posuere. -- Duis. -- Suspendisse a odio fermentum libero. -- Nam enim. Fusce enim. In. -- Maecenas. -- Lorem ipsum primis. -- Curabitur ac turpis semper sed. -- Quisque condimentum. Donec sit. -- Integer convallis non, posuere. -- Etiam vulputate, odio. -- Proin id lorem. Donec quis. -- Curae, Sed nec augue. -- Aliquam ut turpis. \ No newline at end of file diff --git a/test-server/src/main/scala/com/softwaremill/sttp/server/TestHttpServer.scala b/test-server/src/main/scala/com/softwaremill/sttp/server/TestHttpServer.scala deleted file mode 100644 index 906154d..0000000 --- a/test-server/src/main/scala/com/softwaremill/sttp/server/TestHttpServer.scala +++ /dev/null @@ -1,197 +0,0 @@ -package com.softwaremill.sttp.server - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.coding.{Deflate, Gzip, NoCoding} -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.CacheDirectives._ -import akka.http.scaladsl.model.headers._ -import akka.http.scaladsl.server.Directives.{entity, path, _} -import akka.http.scaladsl.server.Route -import akka.http.scaladsl.server.directives.Credentials -import akka.stream.ActorMaterializer -import akka.util.ByteString - -import scala.concurrent.duration._ -import scala.concurrent.{Await, Future} - -object TestHttpServer { - - def main(args: Array[String]): Unit = { - val port = args.headOption.map(_.toInt).getOrElse(51823) - - Await.result(new TestHttpServer(port).start(), 10.seconds) - } -} - -class TestHttpServer(port: Int) extends AutoCloseable { - - import scala.concurrent.ExecutionContext.Implicits.global - - private implicit val actorSystem: ActorSystem = ActorSystem("sttp-test-server") - private implicit val materializer: ActorMaterializer = ActorMaterializer() - - private def paramsToString(m: Map[String, String]): String = - m.toList.sortBy(_._1).map(p => s"${p._1}=${p._2}").mkString(" ") - - private val textFile = new java.io.File("src/main/resources/textfile.txt") - private val binaryFile = new java.io.File("src/main/resources/binaryfile.jpg") - private val textWithSpecialCharacters = "Żółć!" - - val serverRoutes: Route = - pathPrefix("echo") { - pathPrefix("form_params") { - formFieldMap { params => - path("as_string") { - complete(paramsToString(params)) - } ~ - path("as_params") { - complete(FormData(params)) - } - } - } ~ get { - parameterMap { params => - complete(List("GET", "/echo", paramsToString(params)) - .filter(_.nonEmpty) - .mkString(" ")) - } - } ~ - post { - parameterMap { params => - entity(as[String]) { body: String => - complete(List("POST", "/echo", paramsToString(params), body) - .filter(_.nonEmpty) - .mkString(" ")) - } - } - } - } ~ pathPrefix("streaming") { - path("echo") { - post { - parameterMap { _ => - entity(as[String]) { body: String => - complete(body) - } - } - } - } - } ~ path("set_headers") { - get { - respondWithHeader(`Cache-Control`(`max-age`(1000L))) { - respondWithHeader(`Cache-Control`(`no-cache`)) { - complete("ok") - } - } - } - } ~ pathPrefix("set_cookies") { - path("with_expires") { - setCookie(HttpCookie("c", "v", expires = Some(DateTime(1997, 12, 8, 12, 49, 12)))) { - complete("ok") - } - } ~ get { - setCookie( - HttpCookie( - "cookie1", - "value1", - secure = true, - httpOnly = true, - maxAge = Some(123L) - ) - ) { - setCookie(HttpCookie("cookie2", "value2")) { - setCookie( - HttpCookie( - "cookie3", - "", - domain = Some("xyz"), - path = Some("a/b/c") - ) - ) { - complete("ok") - } - } - } - } - } ~ path("secure_basic") { - authenticateBasic("test realm", { - case c @ Credentials.Provided(un) if un == "adam" && c.verify("1234") => - Some(un) - case _ => None - }) { userName => - complete(s"Hello, $userName!") - } - } ~ path("compress") { - encodeResponseWith(Gzip, Deflate, NoCoding) { - complete("I'm compressed!") - } - } ~ pathPrefix("download") { - path("binary") { - getFromFile(binaryFile) - } ~ path("text") { - getFromFile(textFile) - } - } ~ pathPrefix("multipart") { - entity(as[akka.http.scaladsl.model.Multipart.FormData]) { fd => - complete { - fd.parts - .mapAsync(1) { p => - val fv = p.entity.dataBytes.runFold(ByteString())(_ ++ _) - fv.map(_.utf8String) - .map(v => p.name + "=" + v + p.filename.fold("")(fn => s" ($fn)")) - } - .runFold(Vector.empty[String])(_ :+ _) - .map(v => v.mkString(", ")) - } - } - } ~ pathPrefix("redirect") { - path("r1") { - redirect("/redirect/r2", StatusCodes.TemporaryRedirect) - } ~ - path("r2") { - redirect("/redirect/r3", StatusCodes.PermanentRedirect) - } ~ - path("r3") { - redirect("/redirect/r4", StatusCodes.Found) - } ~ - path("r4") { - complete("819") - } ~ - path("loop") { - redirect("/redirect/loop", StatusCodes.Found) - } - } ~ pathPrefix("timeout") { - complete { - akka.pattern.after(1.second, using = actorSystem.scheduler)( - Future.successful("Done") - ) - } - } ~ path("empty_unauthorized_response") { - post { - import akka.http.scaladsl.model._ - complete( - HttpResponse( - status = StatusCodes.Unauthorized, - headers = Nil, - entity = HttpEntity.Empty, - protocol = HttpProtocols.`HTTP/1.1` - )) - } - } ~ path("respond_with_iso_8859_2") { - get { ctx => - val entity = - HttpEntity(MediaTypes.`text/plain`.withCharset(HttpCharset.custom("ISO-8859-2")), textWithSpecialCharacters) - ctx.complete(HttpResponse(200, entity = entity)) - } - } - - def start(): Future[Http.ServerBinding] = { - Http().bindAndHandle(serverRoutes, "localhost", port) - } - - def close(): Unit = { - Await.result( - actorSystem.terminate(), - 10.seconds - ) - } -} -- cgit v1.2.3