aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOmar Alejandro Mainegra Sarduy <omainegra@gmail.com>2017-08-11 17:45:29 -0400
committerOmar Alejandro Mainegra Sarduy <omainegra@gmail.com>2017-08-11 17:45:29 -0400
commitad28cb44c9a39b6bb5b7176358381b19b6e9ac28 (patch)
tree7a0a10f6270e3233633eda1e7f6b90ea4a12085d
parentc6c1f5a34930946e8ab4e9248b9255ce2e1464fe (diff)
parent00e10184c05431d2692d2b54425481a67ee3bc8e (diff)
downloadsttp-ad28cb44c9a39b6bb5b7176358381b19b6e9ac28.tar.gz
sttp-ad28cb44c9a39b6bb5b7176358381b19b6e9ac28.tar.bz2
sttp-ad28cb44c9a39b6bb5b7176358381b19b6e9ac28.zip
Merge branch 'master' into okhttp3-monix
# Conflicts: # build.sbt # okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
-rw-r--r--README.md48
-rw-r--r--akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala13
-rw-r--r--async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/MonixAsyncHttpClientHandler.scala3
-rw-r--r--async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala13
-rw-r--r--build.sbt14
-rw-r--r--circe/src/main/scala/com/softwaremill/sttp/circe.scala17
-rw-r--r--circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala106
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala13
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/RequestT.scala43
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/model/RequestBody.scala41
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala1
-rw-r--r--okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala14
-rw-r--r--project/plugins.sbt2
-rw-r--r--tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala1
-rw-r--r--version.sbt2
15 files changed, 258 insertions, 73 deletions
diff --git a/README.md b/README.md
index 7294dd8..b5a3179 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ If you are an [Ammonite](http://ammonite.io) user, you can quickly start
experimenting with sttp by copy-pasting the following:
```scala
-import $ivy.`com.softwaremill.sttp::core:0.0.5`
+import $ivy.`com.softwaremill.sttp::core:0.0.6`
import com.softwaremill.sttp._
implicit val handler = HttpURLConnectionSttpHandler
sttp.get(uri"http://httpbin.org/ip").send()
@@ -70,7 +70,7 @@ sttp.get(uri"http://httpbin.org/ip").send()
SBT dependency:
```scala
-"com.softwaremill.sttp" %% "core" % "0.0.5"
+"com.softwaremill.sttp" %% "core" % "0.0.6"
```
`sttp` is available for Scala 2.11 and 2.12, and requires Java 8. The core
@@ -209,7 +209,7 @@ implicit val sttpHandler = HttpURLConnectionSttpHandler
To use, add the following dependency to your project:
```scala
-"com.softwaremill.sttp" %% "akka-http-handler" % "0.0.5"
+"com.softwaremill.sttp" %% "akka-http-handler" % "0.0.6"
```
This handler depends on [akka-http](http://doc.akka.io/docs/akka-http/current/scala/http/).
@@ -268,13 +268,13 @@ val response: Future[Response[Source[ByteString, Any]]] =
To use, add the following dependency to your project:
```scala
-"com.softwaremill.sttp" %% "async-http-client-handler-future" % "0.0.5"
+"com.softwaremill.sttp" %% "async-http-client-handler-future" % "0.0.6"
// or
-"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.5"
+"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.6"
// or
-"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.5"
+"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.6"
// or
-"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.5"
+"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.6"
```
This handler depends on [async-http-client](https://github.com/AsyncHttpClient/async-http-client).
@@ -356,7 +356,7 @@ val response: Task[Response[Observable[ByteBuffer]]] =
To use, add the following dependency to your project:
```scala
-"com.softwaremill.sttp" %% "okhttp-client-handler" % "0.0.5"
+"com.softwaremill.sttp" %% "okhttp-client-handler" % "0.0.6"
```
This handler depends on [OkHttp](http://square.github.io/okhttp/), and offers
@@ -375,6 +375,38 @@ with a monad for the response wrapper.
This brings the possibility to `map` and `flatMap` over responses. That way you
could implement e.g. a logging or metric-capturing wrapper.
+## JSON
+
+JSON encoding of bodies and decoding of responses can be handled using
+[Circe](https://circe.github.io/circe/) by the `circe` module. To use
+add the following dependency to your project:
+
+```scala
+"com.softwaremill.sttp" %% "circe" % "0.0.6"
+```
+
+This module adds a method to the request and a function that can be given to
+a request to decode the response to a specific object.
+
+```scala
+import com.softwaremill.sttp._
+import com.softwaremill.sttp.circe._
+
+implicit val handler = HttpURLConnectionSttpHandler
+
+// Assume that there is an implicit circe encoder in scope
+// for the request Payload, and a decoder for the Response
+val requestPayload: Payload = ???
+
+val response: Either[io.circe.Error, Response] =
+ sttp
+ .post(uri"...")
+ .body(requestPayload)
+ .response(asJson[Response])
+ .send()
+```
+
+
## Request type
All request descriptions have type `RequestT[U, T, S]` (T as in Template).
diff --git a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala
index 0a2a467..d1dcc22 100644
--- a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala
+++ b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala
@@ -132,20 +132,19 @@ class AkkaHttpSttpHandler private (actorSystem: ActorSystem,
getContentTypeOrOctetStream(r).map { ct =>
def doSet(body: RequestBody[S]): HttpRequest = body match {
case NoBody => ar
- case StringBody(b, encoding) =>
+ case StringBody(b, encoding, _) =>
val ctWithEncoding = HttpCharsets
.getForKey(encoding)
.map(hc => ContentType.apply(ct.mediaType, () => hc))
.getOrElse(ct)
ar.withEntity(ctWithEncoding, b.getBytes(encoding))
- case ByteArrayBody(b) => ar.withEntity(b)
- case ByteBufferBody(b) => ar.withEntity(ByteString(b))
- case InputStreamBody(b) =>
+ case ByteArrayBody(b, _) => ar.withEntity(b)
+ case ByteBufferBody(b, _) => ar.withEntity(ByteString(b))
+ case InputStreamBody(b, _) =>
ar.withEntity(
HttpEntity(ct, StreamConverters.fromInputStream(() => b)))
- case PathBody(b) => ar.withEntity(ct, b)
- case StreamBody(s) => ar.withEntity(HttpEntity(ct, s))
- case SerializableBody(f, t) => doSet(f(t))
+ case PathBody(b, _) => ar.withEntity(ct, b)
+ case StreamBody(s) => ar.withEntity(HttpEntity(ct, s))
}
doSet(body)
diff --git a/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/MonixAsyncHttpClientHandler.scala b/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/MonixAsyncHttpClientHandler.scala
index c782519..5b6426f 100644
--- a/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/MonixAsyncHttpClientHandler.scala
+++ b/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/MonixAsyncHttpClientHandler.scala
@@ -25,9 +25,8 @@ class MonixAsyncHttpClientHandler private (
closeClient) {
override protected def streamBodyToPublisher(
- s: Observable[ByteBuffer]): Publisher[ByteBuffer] = {
+ s: Observable[ByteBuffer]): Publisher[ByteBuffer] =
s.toReactivePublisher
- }
override protected def publisherToStreamBody(
p: Publisher[ByteBuffer]): Observable[ByteBuffer] =
diff --git a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala
index 8f75030..7b74c29 100644
--- a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala
+++ b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala
@@ -159,24 +159,21 @@ abstract class AsyncHttpClientHandler[R[_], S](asyncHttpClient: AsyncHttpClient,
body match {
case NoBody => // skip
- case StringBody(b, encoding) =>
+ case StringBody(b, encoding, _) =>
rb.setBody(b.getBytes(encoding))
- case ByteArrayBody(b) =>
+ case ByteArrayBody(b, _) =>
rb.setBody(b)
- case ByteBufferBody(b) =>
+ case ByteBufferBody(b, _) =>
rb.setBody(b)
- case InputStreamBody(b) =>
+ case InputStreamBody(b, _) =>
rb.setBody(b)
- case PathBody(b) =>
+ case PathBody(b, _) =>
rb.setBody(b.toFile)
- case SerializableBody(f, t) =>
- setBody(r, f(t), rb)
-
case StreamBody(s) =>
val cl = r.headers
.find(_._1.equalsIgnoreCase(ContentLengthHeader))
diff --git a/build.sbt b/build.sbt
index 8e105a0..588df88 100644
--- a/build.sbt
+++ b/build.sbt
@@ -36,6 +36,8 @@ val akkaHttp = "com.typesafe.akka" %% "akka-http" % akkaHttpVersion
val monixVersion = "2.3.0"
val monix = "io.monix" %% "monix" % monixVersion
+val circeVersion = "0.8.0"
+
val scalaTest = "org.scalatest" %% "scalatest" % "3.0.3"
lazy val rootProject = (project in file("."))
@@ -51,6 +53,7 @@ lazy val rootProject = (project in file("."))
catsAsyncHttpClientHandler,
okhttpClientHandler,
okhttpMonixClientHandler,
+ circe,
tests
)
@@ -136,6 +139,17 @@ lazy val okhttpMonixClientHandler: Project = (project in file(
libraryDependencies ++= Seq(monix)
) dependsOn okhttpClientHandler
+lazy val circe: Project = (project in file("circe"))
+ .settings(commonSettings: _*)
+ .settings(
+ name := "circe",
+ libraryDependencies ++= Seq(
+ "io.circe" %% "circe-core" % circeVersion,
+ "io.circe" %% "circe-parser" % circeVersion,
+ scalaTest % "test"
+ )
+ ) dependsOn core
+
lazy val tests: Project = (project in file("tests"))
.settings(commonSettings: _*)
.settings(
diff --git a/circe/src/main/scala/com/softwaremill/sttp/circe.scala b/circe/src/main/scala/com/softwaremill/sttp/circe.scala
new file mode 100644
index 0000000..95a72e1
--- /dev/null
+++ b/circe/src/main/scala/com/softwaremill/sttp/circe.scala
@@ -0,0 +1,17 @@
+package com.softwaremill.sttp
+
+import com.softwaremill.sttp.model._
+import io.circe.parser._
+import io.circe.{Decoder, Encoder}
+
+package object circe {
+ private[sttp] val ApplicationJsonContentType = "application/json"
+
+ implicit def circeBodySerializer[B](
+ implicit encoder: Encoder[B]): BodySerializer[B] =
+ b => StringBody(encoder(b).noSpaces, Utf8, Some(ApplicationJsonContentType))
+
+ def asJson[B: Decoder]: ResponseAs[Either[io.circe.Error, B], Nothing] =
+ asString(Utf8).map(decode[B])
+
+}
diff --git a/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala b/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
new file mode 100644
index 0000000..e8bbb68
--- /dev/null
+++ b/circe/src/test/scala/com/softwaremill/sttp/CirceTests.scala
@@ -0,0 +1,106 @@
+package com.softwaremill.sttp
+
+import com.softwaremill.sttp.model._
+import io.circe._
+import org.scalatest._
+
+import scala.language.higherKinds
+
+class CirceTests extends FlatSpec with Matchers with EitherValues {
+ import circe._
+
+ "The circe module" should "encode arbitrary bodies given an encoder" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val expected = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+
+ val req = sttp.body(body)
+
+ extractBody(req) shouldBe expected
+ }
+
+ it should "decode arbitrary bodies given a decoder" in {
+ val body = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
+ val expected = Outer(Inner(42, true, "horses"), "cats")
+
+ val responseAs = asJson[Outer]
+
+ runJsonResponseAs(responseAs)(body).right.value shouldBe expected
+ }
+
+ it should "fail to decode invalid json" in {
+ val body = """not valid json"""
+
+ val responseAs = asJson[Outer]
+
+ runJsonResponseAs(responseAs)(body).left.value shouldBe an[io.circe.Error]
+ }
+
+ it should "encode and decode back to the same thing" in {
+ val outer = Outer(Inner(42, true, "horses"), "cats")
+
+ val encoded = extractBody(sttp.body(outer))
+ val decoded = runJsonResponseAs(asJson[Outer])(encoded)
+
+ decoded.right.value shouldBe outer
+ }
+
+ it should "set the content type" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val req = sttp.body(body)
+
+ val ct = req.headers.toMap.get("Content-Type")
+
+ ct shouldBe Some(contentTypeWithEncoding(ApplicationJsonContentType, Utf8))
+ }
+
+ it should "only set the content type if it was not set earlier" in {
+ val body = Outer(Inner(42, true, "horses"), "cats")
+ val req = sttp.contentType("horses/cats").body(body)
+
+ val ct = req.headers.toMap.get("Content-Type")
+
+ ct shouldBe Some("horses/cats")
+ }
+
+ case class Inner(a: Int, b: Boolean, c: String)
+
+ object Inner {
+ implicit val encoder: Encoder[Inner] =
+ Encoder.forProduct3("a", "b", "c")(i => (i.a, i.b, i.c))
+ implicit val decoder: Decoder[Inner] =
+ Decoder.forProduct3("a", "b", "c")(Inner.apply)
+ }
+
+ case class Outer(foo: Inner, bar: String)
+
+ object Outer {
+ implicit val encoder: Encoder[Outer] =
+ Encoder.forProduct2("foo", "bar")(o => (o.foo, o.bar))
+ implicit val decoder: Decoder[Outer] =
+ Decoder.forProduct2("foo", "bar")(Outer.apply)
+ }
+
+ def extractBody[A[_], B, C](request: RequestT[A, B, C]): String =
+ request.body match {
+ case StringBody(body, "utf-8", Some(ApplicationJsonContentType)) =>
+ body
+ case wrongBody =>
+ fail(
+ s"Request body does not serialize to correct StringBody: $wrongBody")
+ }
+
+ def runJsonResponseAs[A](responseAs: ResponseAs[A, Nothing]): String => A =
+ responseAs match {
+ case responseAs: MappedResponseAs[_, A, Nothing] =>
+ responseAs.raw match {
+ case ResponseAsString("utf-8") =>
+ responseAs.g
+ case ResponseAsString(encoding) =>
+ fail(
+ s"MappedResponseAs wraps a ResponseAsString with wrong encoding: $encoding")
+ case _ =>
+ fail("MappedResponseAs does not wrap a ResponseAsString")
+ }
+ case _ => fail("ResponseAs is not a MappedResponseAs")
+ }
+}
diff --git a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala
index 29da886..dd208f4 100644
--- a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala
@@ -56,28 +56,25 @@ object HttpURLConnectionSttpHandler extends SttpHandler[Id, Nothing] {
body match {
case NoBody => // skip
- case StringBody(b, encoding) =>
+ case StringBody(b, encoding, _) =>
val writer = new OutputStreamWriter(c.getOutputStream, encoding)
try writer.write(b)
finally writer.close()
- case ByteArrayBody(b) =>
+ case ByteArrayBody(b, _) =>
c.getOutputStream.write(b)
- case ByteBufferBody(b) =>
+ case ByteBufferBody(b, _) =>
val channel = Channels.newChannel(c.getOutputStream)
try channel.write(b)
finally channel.close()
- case InputStreamBody(b) =>
+ case InputStreamBody(b, _) =>
copyStream(b, c.getOutputStream)
- case PathBody(b) =>
+ case PathBody(b, _) =>
Files.copy(b, c.getOutputStream)
- case SerializableBody(f, t) =>
- setBody(f(t), c)
-
case StreamBody(s) =>
// we have an instance of nothing - everything's possible!
s
diff --git a/core/src/main/scala/com/softwaremill/sttp/RequestT.scala b/core/src/main/scala/com/softwaremill/sttp/RequestT.scala
index ff80dd5..b785e7c 100644
--- a/core/src/main/scala/com/softwaremill/sttp/RequestT.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/RequestT.scala
@@ -100,10 +100,8 @@ case class RequestT[U[_], T, +S](
* bytes in the string using the given encoding.
*/
def body(b: String, encoding: String): RequestT[U, T, S] =
- setContentTypeIfMissing(
- contentTypeWithEncoding(TextPlainContentType, encoding))
+ withBasicBody(StringBody(b, encoding))
.setContentLengthIfMissing(b.getBytes(encoding).length)
- .copy(body = StringBody(b, encoding))
/**
* If content type is not yet specified, will be set to
@@ -113,25 +111,22 @@ case class RequestT[U[_], T, +S](
* of the given array.
*/
def body(b: Array[Byte]): RequestT[U, T, S] =
- setContentTypeIfMissing(ApplicationOctetStreamContentType)
+ withBasicBody(ByteArrayBody(b))
.setContentLengthIfMissing(b.length)
- .copy(body = ByteArrayBody(b))
/**
* If content type is not yet specified, will be set to
* `application/octet-stream`.
*/
def body(b: ByteBuffer): RequestT[U, T, S] =
- setContentTypeIfMissing(ApplicationOctetStreamContentType).copy(
- body = ByteBufferBody(b))
+ withBasicBody(ByteBufferBody(b))
/**
* If content type is not yet specified, will be set to
* `application/octet-stream`.
*/
def body(b: InputStream): RequestT[U, T, S] =
- setContentTypeIfMissing(ApplicationOctetStreamContentType).copy(
- body = InputStreamBody(b))
+ withBasicBody(InputStreamBody(b))
/**
* If content type is not yet specified, will be set to
@@ -151,9 +146,8 @@ case class RequestT[U[_], T, +S](
* of the given file.
*/
def body(b: Path): RequestT[U, T, S] =
- setContentTypeIfMissing(ApplicationOctetStreamContentType)
+ withBasicBody(PathBody(b))
.setContentLengthIfMissing(b.toFile.length())
- .copy(body = PathBody(b))
/**
* Encodes the given parameters as form data using `utf-8`.
@@ -204,13 +198,10 @@ case class RequestT[U[_], T, +S](
* `application/octet-stream`.
*/
def body[B: BodySerializer](b: B): RequestT[U, T, S] =
- setContentTypeIfMissing(ApplicationOctetStreamContentType).copy(
- body = SerializableBody(implicitly[BodySerializer[B]], b))
-
- //def multipartData(parts: MultiPart*): RequestTemplate[U] = ???
+ withBasicBody(implicitly[BodySerializer[B]].apply(b))
def streamBody[S2 >: S](b: S2): RequestT[U, T, S2] =
- this.copy[U, T, S2](body = StreamBody(b))
+ copy[U, T, S2](body = StreamBody(b))
/**
* What's the target type to which the response body should be read.
@@ -239,6 +230,17 @@ case class RequestT[U[_], T, +S](
private def setContentTypeIfMissing(ct: String): RequestT[U, T, S] =
if (hasContentType) this else contentType(ct)
+ private def withBasicBody(body: BasicRequestBody) = {
+ val defaultCt = body match {
+ case StringBody(_, encoding, Some(ct)) =>
+ Some(contentTypeWithEncoding(ct, encoding))
+ case _ =>
+ body.defaultContentType
+ }
+
+ defaultCt.fold(this)(setContentTypeIfMissing).copy(body = body)
+ }
+
private def hasContentLength: Boolean =
headers.exists(_._1.equalsIgnoreCase(ContentLengthHeader))
private def setContentLengthIfMissing(l: => Long): RequestT[U, T, S] =
@@ -247,10 +249,11 @@ case class RequestT[U[_], T, +S](
private def formDataBody(fs: Seq[(String, String)],
encoding: String): RequestT[U, T, S] = {
val b = fs
- .map(
- p =>
- URLEncoder.encode(p._1, encoding) + "=" + URLEncoder
- .encode(p._2, encoding))
+ .map {
+ case (key, value) =>
+ URLEncoder.encode(key, encoding) + "=" +
+ URLEncoder.encode(value, encoding)
+ }
.mkString("&")
setContentTypeIfMissing(ApplicationFormContentType)
.setContentLengthIfMissing(b.getBytes(encoding).length)
diff --git a/core/src/main/scala/com/softwaremill/sttp/model/RequestBody.scala b/core/src/main/scala/com/softwaremill/sttp/model/RequestBody.scala
index 0c737f0..7499048 100644
--- a/core/src/main/scala/com/softwaremill/sttp/model/RequestBody.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/model/RequestBody.scala
@@ -4,18 +4,39 @@ import java.io.InputStream
import java.nio.ByteBuffer
import java.nio.file.Path
-import com.softwaremill.sttp.BodySerializer
+import com.softwaremill.sttp._
sealed trait RequestBody[+S]
case object NoBody extends RequestBody[Nothing]
-case class SerializableBody[T](f: BodySerializer[T], t: T)
- extends RequestBody[Nothing]
-
-sealed trait BasicRequestBody extends RequestBody[Nothing]
-case class StringBody(s: String, encoding: String) extends BasicRequestBody
-case class ByteArrayBody(b: Array[Byte]) extends BasicRequestBody
-case class ByteBufferBody(b: ByteBuffer) extends BasicRequestBody
-case class InputStreamBody(b: InputStream) extends BasicRequestBody
-case class PathBody(f: Path) extends BasicRequestBody
+
+sealed trait BasicRequestBody extends RequestBody[Nothing] {
+ def defaultContentType: Option[String]
+}
+
+case class StringBody(
+ s: String,
+ encoding: String,
+ defaultContentType: Option[String] = Some(TextPlainContentType)
+) extends BasicRequestBody
+
+case class ByteArrayBody(
+ b: Array[Byte],
+ defaultContentType: Option[String] = Some(ApplicationOctetStreamContentType)
+) extends BasicRequestBody
+
+case class ByteBufferBody(
+ b: ByteBuffer,
+ defaultContentType: Option[String] = Some(ApplicationOctetStreamContentType)
+) extends BasicRequestBody
+
+case class InputStreamBody(
+ b: InputStream,
+ defaultContentType: Option[String] = Some(ApplicationOctetStreamContentType)
+) extends BasicRequestBody
+
+case class PathBody(
+ f: Path,
+ defaultContentType: Option[String] = Some(ApplicationOctetStreamContentType)
+) extends BasicRequestBody
case class StreamBody[S](s: S) extends RequestBody[S]
diff --git a/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala b/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala
index 24b9b2b..5a008ed 100644
--- a/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala
@@ -2,7 +2,6 @@ package com.softwaremill.sttp.model
import java.io.{File, FileOutputStream, IOException, InputStream}
import java.net.URLDecoder
-import java.nio.file.Path
import com.softwaremill.sttp.{MonadError, transfer}
diff --git a/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
index 2090e02..8f21eb0 100644
--- a/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
+++ b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
@@ -49,19 +49,19 @@ abstract class OkHttpClientHandler[R[_], S](client: OkHttpClient)
private def setBody(requestBody: RequestBody[S]): Option[OkHttpRequestBody] = {
requestBody match {
case NoBody => None
- case StringBody(b, encoding) =>
+ case StringBody(b, encoding, _) =>
Some(OkHttpRequestBody.create(MediaType.parse(encoding), b))
- case ByteArrayBody(b) => Some(OkHttpRequestBody.create(null, b))
- case ByteBufferBody(b) => Some(OkHttpRequestBody.create(null, b.array()))
- case InputStreamBody(b) =>
+ case ByteArrayBody(b, _) => Some(OkHttpRequestBody.create(null, b))
+ case ByteBufferBody(b, _) =>
+ Some(OkHttpRequestBody.create(null, b.array()))
+ case InputStreamBody(b, _) =>
Some(new OkHttpRequestBody() {
override def writeTo(sink: BufferedSink): Unit =
sink.writeAll(Okio.source(b))
override def contentType(): MediaType = null
})
- case PathBody(b) => Some(OkHttpRequestBody.create(null, b.toFile))
- case SerializableBody(f, t) => setBody(f(t))
- case StreamBody(s) => streamToRequestBody(s)
+ case PathBody(b, _) => Some(OkHttpRequestBody.create(null, b.toFile))
+ case StreamBody(s) => streamToRequestBody(s)
}
}
diff --git a/project/plugins.sbt b/project/plugins.sbt
index a6c8f63..b262eb5 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,4 +1,4 @@
-addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.9")
+addSbtPlugin("com.lucidchart" % "sbt-scalafmt" % "1.10")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1")
diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
index d8e3d03..9b64271 100644
--- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
+++ b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala
@@ -183,6 +183,7 @@ class BasicTests
cookiesTests()
authTests()
compressionTests()
+ downloadFileTests()
def parseResponseTests(): Unit = {
name should "parse response as string" in {
diff --git a/version.sbt b/version.sbt
index 3b1722c..c4b2a15 100644
--- a/version.sbt
+++ b/version.sbt
@@ -1 +1 @@
-version in ThisBuild := "0.0.6-SNAPSHOT"
+version in ThisBuild := "0.0.7-SNAPSHOT"