From 71f6a1eeee412045cc08ce8894194573362cb8f0 Mon Sep 17 00:00:00 2001 From: adamw Date: Thu, 31 Aug 2017 14:32:01 +0200 Subject: Response.body is now an Either[String, T], to handle cases when the status code isn't 2xx --- README.md | 4 +- .../sttp/akkahttp/AkkaHttpHandler.scala | 10 ++- .../cats/AsyncHttpClientCatsHandler.scala | 4 ++ .../fs2/AsyncHttpClientFs2Handler.scala | 11 +++- .../future/AsyncHttpClientFutureHandler.scala | 4 ++ .../monix/AsyncHttpClientMonixHandler.scala | 12 +++- .../scalaz/AsyncHttpClientScalazHandler.scala | 4 ++ .../asynchttpclient/AsyncHttpClientHandler.scala | 27 ++++++-- .../sttp/HttpURLConnectionHandler.scala | 12 +++- .../scala/com/softwaremill/sttp/Response.scala | 23 ++++++- .../main/scala/com/softwaremill/sttp/package.scala | 8 +++ .../scala/com/softwaremill/sttp/RequestTests.scala | 2 +- .../sttp/okhttp/OkHttpClientHandler.scala | 14 +++- .../scala/com/softwaremill/sttp/BasicTests.scala | 74 ++++++++++++---------- .../com/softwaremill/sttp/StreamingTests.scala | 6 +- 15 files changed, 160 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 4670ea5..603b99d 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,8 @@ val response = request.send() // response.header(...): Option[String] println(response.header("Content-Length")) -// response.body: by default read into a String -println(response.body) +// response.unsafeBody: by default read into a String +println(response.unsafeBody) ``` ## Goals of the project diff --git a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpHandler.scala b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpHandler.scala index 5691d3c..35f8f81 100644 --- a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpHandler.scala +++ b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpHandler.scala @@ -41,8 +41,14 @@ class AkkaHttpHandler private (actorSystem: ActorSystem, .flatMap(Http().singleRequest(_)) .flatMap { hr => val code = hr.status.intValue() - bodyFromAkka(r.response, decodeAkkaResponse(hr)) - .map(Response(_, code, headersFromAkka(hr))) + + val body = if (codeIsSuccess(code)) { + bodyFromAkka(r.response, decodeAkkaResponse(hr)).map(Right(_)) + } else { + bodyFromAkka(asString, decodeAkkaResponse(hr)).map(Left(_)) + } + + body.map(Response(_, code, headersFromAkka(hr))) } } diff --git a/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsHandler.scala b/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsHandler.scala index f6eaf10..fec0eab 100644 --- a/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsHandler.scala +++ b/async-http-client-handler/cats/src/main/scala/com/softwaremill/sttp/asynchttpclient/cats/AsyncHttpClientCatsHandler.scala @@ -28,6 +28,10 @@ class AsyncHttpClientCatsHandler[F[_]: Async] private ( override protected def publisherToStreamBody( p: Publisher[ByteBuffer]): Nothing = throw new IllegalStateException("This handler does not support streaming") + + override protected def publisherToString( + p: Publisher[ByteBuffer]): F[String] = + throw new IllegalStateException("This handler does not support streaming") } object AsyncHttpClientCatsHandler { diff --git a/async-http-client-handler/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Handler.scala b/async-http-client-handler/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Handler.scala index f7ed00c..dc30925 100644 --- a/async-http-client-handler/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Handler.scala +++ b/async-http-client-handler/fs2/src/main/scala/com/softwaremill/sttp/asynchttpclient/fs2/AsyncHttpClientFs2Handler.scala @@ -4,7 +4,7 @@ import java.nio.ByteBuffer import cats.effect._ import com.softwaremill.sttp.asynchttpclient.AsyncHttpClientHandler -import com.softwaremill.sttp.{MonadAsyncError, SttpHandler} +import com.softwaremill.sttp.{MonadAsyncError, SttpHandler, Utf8, concatByteBuffers} import fs2._ import fs2.interop.reactivestreams._ import org.asynchttpclient.{ @@ -33,6 +33,15 @@ class AsyncHttpClientFs2Handler[F[_]: Effect] private ( override protected def publisherToStreamBody( p: Publisher[ByteBuffer]): Stream[F, ByteBuffer] = p.toStream[F] + + override protected def publisherToString( + p: Publisher[ByteBuffer]): F[String] = { + val bytes = p + .toStream[F] + .runFold(ByteBuffer.allocate(0))(concatByteBuffers) + + implicitly[Effect[F]].map(bytes)(bb => new String(bb.array(), Utf8)) + } } object AsyncHttpClientFs2Handler { diff --git a/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/AsyncHttpClientFutureHandler.scala b/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/AsyncHttpClientFutureHandler.scala index d0bc03e..81dee9e 100644 --- a/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/AsyncHttpClientFutureHandler.scala +++ b/async-http-client-handler/future/src/main/scala/com/softwaremill/sttp/asynchttpclient/future/AsyncHttpClientFutureHandler.scala @@ -26,6 +26,10 @@ class AsyncHttpClientFutureHandler private ( override protected def publisherToStreamBody( p: Publisher[ByteBuffer]): Nothing = throw new IllegalStateException("This handler does not support streaming") + + override protected def publisherToString( + p: Publisher[ByteBuffer]): Future[String] = + throw new IllegalStateException("This handler does not support streaming") } object AsyncHttpClientFutureHandler { diff --git a/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixHandler.scala b/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixHandler.scala index 7f01f0c..1ef3973 100644 --- a/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixHandler.scala +++ b/async-http-client-handler/monix/src/main/scala/com/softwaremill/sttp/asynchttpclient/monix/AsyncHttpClientMonixHandler.scala @@ -2,7 +2,7 @@ package com.softwaremill.sttp.asynchttpclient.monix import java.nio.ByteBuffer -import com.softwaremill.sttp.{MonadAsyncError, SttpHandler} +import com.softwaremill.sttp.{MonadAsyncError, SttpHandler, Utf8, concatByteBuffers} import com.softwaremill.sttp.asynchttpclient.AsyncHttpClientHandler import monix.eval.Task import monix.execution.{Cancelable, Scheduler} @@ -31,6 +31,16 @@ class AsyncHttpClientMonixHandler private ( override protected def publisherToStreamBody( p: Publisher[ByteBuffer]): Observable[ByteBuffer] = Observable.fromReactivePublisher(p) + + override protected def publisherToString( + p: Publisher[ByteBuffer]): Task[String] = { + + val bytes = Observable + .fromReactivePublisher(p) + .foldLeftL(ByteBuffer.allocate(0))(concatByteBuffers) + + bytes.map(bb => new String(bb.array(), Utf8)) + } } object AsyncHttpClientMonixHandler { diff --git a/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazHandler.scala b/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazHandler.scala index badccb3..4bc169d 100644 --- a/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazHandler.scala +++ b/async-http-client-handler/scalaz/src/main/scala/com/softwaremill/sttp/asynchttpclient/scalaz/AsyncHttpClientScalazHandler.scala @@ -26,6 +26,10 @@ class AsyncHttpClientScalazHandler private (asyncHttpClient: AsyncHttpClient, override protected def publisherToStreamBody( p: Publisher[ByteBuffer]): Nothing = throw new IllegalStateException("This handler does not support streaming") + + override protected def publisherToString( + p: Publisher[ByteBuffer]): Task[String] = + throw new IllegalStateException("This handler does not support streaming") } object AsyncHttpClientScalazHandler { 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 2e5d16b..f8122cc 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 @@ -62,6 +62,8 @@ abstract class AsyncHttpClientHandler[R[_], S](asyncHttpClient: AsyncHttpClient, protected def publisherToStreamBody(p: Publisher[ByteBuffer]): S + protected def publisherToString(p: Publisher[ByteBuffer]): R[String] + private def eagerAsyncHandler[T]( responseAs: ResponseAs[T, S], success: R[Response[T]] => Unit, @@ -134,8 +136,15 @@ abstract class AsyncHttpClientHandler[R[_], S](asyncHttpClient: AsyncHttpClient, val baseResponse = readResponseNoBody(builder.build()) val p = publisher.getOrElse(EmptyPublisher) val s = publisherToStreamBody(p) - val t = responseAs.responseIsStream(s) - success(rm.unit(baseResponse.copy(body = t))) + val b = if (codeIsSuccess(baseResponse.code)) { + rm.unit(Right(responseAs.responseIsStream(s))) + } else { + rm.map(publisherToString(p), Left(_: String)) + } + + success(rm.map(b, { bb: Either[String, T] => + baseResponse.copy(body = bb) + })) } } @@ -223,12 +232,20 @@ abstract class AsyncHttpClientHandler[R[_], S](asyncHttpClient: AsyncHttpClient, private def readEagerResponse[T]( response: AsyncResponse, responseAs: ResponseAs[T, S]): R[Response[T]] = { - val body = eagerResponseHandler(response).handle(responseAs, rm) - rm.map(body, (b: T) => readResponseNoBody(response).copy(body = b)) + val base = readResponseNoBody(response) + + val body = if (codeIsSuccess(base.code)) { + rm.map(eagerResponseHandler(response).handle(responseAs, rm), Right(_: T)) + } else { + rm.map(eagerResponseHandler(response).handle(asString, rm), + Left(_: String)) + } + + rm.map(body, (b: Either[String, T]) => base.copy(body = b)) } private def readResponseNoBody(response: AsyncResponse): Response[Unit] = { - Response((), + Response(Right(()), response.getStatusCode, response.getHeaders .iterator() diff --git a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionHandler.scala b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionHandler.scala index 548dd9b..ddc1293 100644 --- a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionHandler.scala +++ b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionHandler.scala @@ -191,9 +191,15 @@ object HttpURLConnectionHandler extends SttpHandler[Id, Nothing] { .filter(_._1 != null) .flatMap { case (k, vv) => vv.asScala.map((k, _)) } val contentEncoding = Option(c.getHeaderField(ContentEncodingHeader)) - Response(readResponseBody(wrapInput(contentEncoding, is), responseAs), - c.getResponseCode, - headers) + val code = c.getResponseCode + val wrappedIs = wrapInput(contentEncoding, is) + val body = if (codeIsSuccess(code)) { + Right(readResponseBody(wrappedIs, responseAs)) + } else { + Left(readResponseBody(wrappedIs, asString)) + } + + Response(body, code, headers) } private def readResponseBody[T](is: InputStream, diff --git a/core/src/main/scala/com/softwaremill/sttp/Response.scala b/core/src/main/scala/com/softwaremill/sttp/Response.scala index 44c39c3..5a3cfe3 100644 --- a/core/src/main/scala/com/softwaremill/sttp/Response.scala +++ b/core/src/main/scala/com/softwaremill/sttp/Response.scala @@ -15,9 +15,18 @@ import scala.collection.JavaConverters._ import scala.collection.immutable.Seq import scala.util.Try -case class Response[T](body: T, code: Int, headers: Seq[(String, String)]) { +/** + * @param body `Right(T)`, if the request was successful (status code 2xx). + * The body is then handled as specified in the request. + * `Left(String)`, if the request wasn't successful (status code + * 3xx, 4xx or 5xx). In this case, the response body is read into + * a `String`. + */ +case class Response[T](body: Either[String, T], + code: Int, + headers: Seq[(String, String)]) { def is200: Boolean = code == 200 - def isSuccess: Boolean = code >= 200 && code < 300 + def isSuccess: Boolean = codeIsSuccess(code) def isRedirect: Boolean = code >= 300 && code < 400 def isClientError: Boolean = code >= 400 && code < 500 def isServerError: Boolean = code >= 500 && code < 600 @@ -34,6 +43,16 @@ case class Response[T](body: T, code: Int, headers: Seq[(String, String)]) { def cookies: Seq[Cookie] = headers(SetCookieHeader) .flatMap(h => HttpCookie.parse(h).asScala.map(hc => Cookie.apply(hc, h))) + + /** + * Get the body of the response. If the status code wasn't 2xx (and there's + * no body to return), an exception is thrown, containing the status code + * and the response from the server. + */ + def unsafeBody: T = body match { + case Left(v) => throw new NoSuchElementException(s"Status code $code: $v") + case Right(v) => v + } } case class Cookie(name: String, diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala index 3c4e844..24eb32c 100644 --- a/core/src/main/scala/com/softwaremill/sttp/package.scala +++ b/core/src/main/scala/com/softwaremill/sttp/package.scala @@ -251,6 +251,14 @@ package object sttp { transfer() } + private[sttp] def codeIsSuccess(c: Int): Boolean = c >= 200 && c < 300 + + private[sttp] def concatByteBuffers(bb1: ByteBuffer, bb2: ByteBuffer): ByteBuffer = + ByteBuffer + .allocate(bb1.array().length + bb2.array().length) + .put(bb1) + .put(bb2) + // uri interpolator implicit class UriContext(val sc: StringContext) extends AnyVal { diff --git a/core/src/test/scala/com/softwaremill/sttp/RequestTests.scala b/core/src/test/scala/com/softwaremill/sttp/RequestTests.scala index c6132c9..506167c 100644 --- a/core/src/test/scala/com/softwaremill/sttp/RequestTests.scala +++ b/core/src/test/scala/com/softwaremill/sttp/RequestTests.scala @@ -31,7 +31,7 @@ class RequestTests extends FlatSpec with Matchers { it should "set cookies from a response" in { val response = - Response((), + Response(Right(()), 0, List((SetCookieHeader, "k1=v1"), (SetCookieHeader, "k2=v2"))) sttp diff --git a/okhttp-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala b/okhttp-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala index b95bf8a..af44132 100644 --- a/okhttp-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala +++ b/okhttp-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala @@ -86,7 +86,15 @@ abstract class OkHttpHandler[R[_], S](client: OkHttpClient) res: OkHttpResponse, responseAs: ResponseAs[T, S]): R[Response[T]] = { - val body = responseHandler(res).handle(responseAs, responseMonad) + val code = res.code() + + val body = if (codeIsSuccess(code)) { + responseMonad.map(responseHandler(res).handle(responseAs, responseMonad), + Right(_: T)) + } else { + responseMonad.map(responseHandler(res).handle(asString, responseMonad), + Left(_: String)) + } val headers = res .headers() @@ -94,7 +102,9 @@ abstract class OkHttpHandler[R[_], S](client: OkHttpClient) .asScala .flatMap(name => res.headers().values(name).asScala.map((name, _))) - responseMonad.map(body, Response(_: T, res.code(), headers.toList)) + responseMonad.map( + body, + Response(_: Either[String, T], res.code(), headers.toList)) } private def responseHandler(res: OkHttpResponse) = diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala index 61b9c19..39340de 100644 --- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala +++ b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala @@ -155,7 +155,7 @@ class BasicTests redirect("/redirect/r3", StatusCodes.PermanentRedirect) } ~ path("r3") { - complete("ok") + complete("819") } } @@ -209,7 +209,7 @@ class BasicTests def parseResponseTests(): Unit = { name should "parse response as string" in { val response = postEcho.body(testBody).send().force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } name should "parse response as string with mapping using map" in { @@ -218,7 +218,7 @@ class BasicTests .response(asString.map(_.length)) .send() .force() - response.body should be(expectedPostEchoResponse.length) + response.unsafeBody should be(expectedPostEchoResponse.length) } name should "parse response as string with mapping using mapResponse" in { @@ -227,13 +227,13 @@ class BasicTests .mapResponse(_.length) .send() .force() - response.body should be(expectedPostEchoResponse.length) + response.unsafeBody should be(expectedPostEchoResponse.length) } name should "parse response as a byte array" in { val response = postEcho.body(testBody).response(asByteArray).send().force() - val fc = new String(response.body, "UTF-8") + val fc = new String(response.unsafeBody, "UTF-8") fc should be(expectedPostEchoResponse) } @@ -245,7 +245,7 @@ class BasicTests .response(asParams) .send() .force() - response.body.toList should be(params) + response.unsafeBody.toList should be(params) } } @@ -256,20 +256,20 @@ class BasicTests .send() .force() - response.body should be("GET /echo p1=v1 p2=v2") + response.unsafeBody should be("GET /echo p1=v1 p2=v2") } } def bodyTests(): Unit = { name should "post a string" in { val response = postEcho.body(testBody).send().force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } name should "post a byte array" in { val response = postEcho.body(testBodyBytes).send().force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } name should "post an input stream" in { @@ -277,7 +277,7 @@ class BasicTests .body(new ByteArrayInputStream(testBodyBytes)) .send() .force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } name should "post a byte buffer" in { @@ -285,14 +285,14 @@ class BasicTests .body(ByteBuffer.wrap(testBodyBytes)) .send() .force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } name should "post a file" in { val f = File.newTemporaryFile().write(testBody) try { val response = postEcho.body(f.toJava).send().force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } finally f.delete() } @@ -301,7 +301,7 @@ class BasicTests try { val response = postEcho.body(f.toJava.toPath).send().force() - response.body should be(expectedPostEchoResponse) + response.unsafeBody should be(expectedPostEchoResponse) } finally f.delete() } @@ -311,7 +311,7 @@ class BasicTests .body("a" -> "b", "c" -> "d") .send() .force() - response.body should be("a=b c=d") + response.unsafeBody should be("a=b c=d") } name should "post form data with special characters" in { @@ -320,12 +320,12 @@ class BasicTests .body("a=" -> "/b", "c:" -> "/d") .send() .force() - response.body should be("a==/b c:=/d") + response.unsafeBody should be("a==/b c:=/d") } name should "post without a body" in { val response = postEcho.send().force() - response.body should be("POST /echo") + response.unsafeBody should be("POST /echo") } } @@ -352,6 +352,7 @@ class BasicTests val response = getHeaders.response(sttpIgnore).send().force() response.code should be(405) response.isClientError should be(true) + response.body should be('left) } } @@ -412,7 +413,7 @@ class BasicTests val req = secureBasic.auth.basic("adam", "1234") val resp = req.send().force() resp.code should be(200) - resp.body should be("Hello, adam!") + resp.unsafeBody should be("Hello, adam!") } } @@ -423,28 +424,28 @@ class BasicTests name should "decompress using the default accept encoding header" in { val req = compress val resp = req.send().force() - resp.body should be(decompressedBody) + resp.unsafeBody should be(decompressedBody) } name should "decompress using gzip" in { val req = compress.header("Accept-Encoding", "gzip", replaceExisting = true) val resp = req.send().force() - resp.body should be(decompressedBody) + resp.unsafeBody should be(decompressedBody) } name should "decompress using deflate" in { val req = compress.header("Accept-Encoding", "deflate", replaceExisting = true) val resp = req.send().force() - resp.body should be(decompressedBody) + resp.unsafeBody should be(decompressedBody) } name should "work despite providing an unsupported encoding" in { val req = compress.header("Accept-Encoding", "br", replaceExisting = true) val resp = req.send().force() - resp.body should be(decompressedBody) + resp.unsafeBody should be(decompressedBody) } } @@ -457,7 +458,7 @@ class BasicTests sttp.get(uri"$endpoint/download/binary").response(asFile(file)) val resp = req.send().force() - resp.body shouldBe file + resp.unsafeBody shouldBe file file should exist file should haveSameContentAs(binaryFile) } @@ -468,7 +469,7 @@ class BasicTests sttp.get(uri"$endpoint/download/text").response(asFile(file)) val resp = req.send().force() - resp.body shouldBe file + resp.unsafeBody shouldBe file file should exist file should haveSameContentAs(textFile) } @@ -479,7 +480,7 @@ class BasicTests sttp.get(uri"$endpoint/download/binary").response(asPath(path)) val resp = req.send().force() - resp.body shouldBe path + resp.unsafeBody shouldBe path path.toFile should exist path.toFile should haveSameContentAs(binaryFile) } @@ -490,7 +491,7 @@ class BasicTests sttp.get(uri"$endpoint/download/text").response(asPath(path)) val resp = req.send().force() - resp.body shouldBe path + resp.unsafeBody shouldBe path path.toFile should exist path.toFile should haveSameContentAs(textFile) } @@ -531,7 +532,7 @@ class BasicTests .response(asPath(path, overwrite = true)) val resp = req.send().force() - resp.body shouldBe path + resp.unsafeBody shouldBe path path.toFile should exist path.toFile should haveSameContentAs(textFile) } @@ -543,14 +544,14 @@ class BasicTests name should "send a multipart message" in { val req = mp.multipartBody(multipart("p1", "v1"), multipart("p2", "v2")) val resp = req.send().force() - resp.body should be("p1=v1, p2=v2") + resp.unsafeBody should be("p1=v1, p2=v2") } name should "send a multipart message with filenames" in { val req = mp.multipartBody(multipart("p1", "v1").fileName("f1"), multipart("p2", "v2").fileName("f2")) val resp = req.send().force() - resp.body should be("p1=v1 (f1), p2=v2 (f2)") + resp.unsafeBody should be("p1=v1 (f1), p2=v2 (f2)") } name should "send a multipart message with a file" in { @@ -559,7 +560,7 @@ class BasicTests val req = mp.multipartBody(multipart("p1", f.toJava), multipart("p2", "v2")) val resp = req.send().force() - resp.body should be(s"p1=$testBody (${f.name}), p2=v2") + resp.unsafeBody should be(s"p1=$testBody (${f.name}), p2=v2") } finally f.delete() } } @@ -567,29 +568,36 @@ class BasicTests def redirectTests(): Unit = { val r1 = sttp.post(uri"$endpoint/redirect/r1") val r2 = sttp.post(uri"$endpoint/redirect/r2") + val r3response = "819" name should "not redirect when redirects shouldn't be followed (temporary)" in { val resp = r1.followRedirects(false).send().force() resp.code should be(307) - resp.body should not be ("ok") + resp.body should be('left) } name should "not redirect when redirects shouldn't be followed (permanent)" in { val resp = r2.followRedirects(false).send().force() resp.code should be(308) - resp.body should not be ("ok") + resp.body should be('left) } name should "redirect when redirects should be followed" in { val resp = r2.send().force() resp.code should be(200) - resp.body should be("ok") + resp.unsafeBody should be(r3response) } name should "redirect twice when redirects should be followed" in { val resp = r1.send().force() resp.code should be(200) - resp.body should be("ok") + resp.unsafeBody should be(r3response) + } + + name should "redirect when redirects should be followed, and the response is parsed" in { + val resp = r2.response(asString.map(_.toInt)).send().force() + resp.code should be(200) + resp.unsafeBody should be(r3response.toInt) } } } diff --git a/tests/src/test/scala/com/softwaremill/sttp/StreamingTests.scala b/tests/src/test/scala/com/softwaremill/sttp/StreamingTests.scala index bb198b8..1f6b78c 100644 --- a/tests/src/test/scala/com/softwaremill/sttp/StreamingTests.scala +++ b/tests/src/test/scala/com/softwaremill/sttp/StreamingTests.scala @@ -52,7 +52,7 @@ class StreamingTests .send() .force() - response.body shouldBe body + response.unsafeBody shouldBe body } it should "receive a stream" in { @@ -63,7 +63,7 @@ class StreamingTests .send() .force() - bodyConsumer(response.body).force() shouldBe body + bodyConsumer(response.unsafeBody).force() shouldBe body } it should "receive a stream from an https site" in { @@ -76,7 +76,7 @@ class StreamingTests .send() .force() - bodyConsumer(response.body).force() should include("") + bodyConsumer(response.unsafeBody).force() should include("") } } -- cgit v1.2.3