aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala18
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/HttpConnectionSttpHandler.scala6
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/SttpHandler.scala33
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/model/package.scala14
-rw-r--r--core/src/main/scala/com/softwaremill/sttp/package.scala72
5 files changed, 61 insertions, 82 deletions
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 7c36da6..668da90 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
@@ -14,27 +14,22 @@ import com.softwaremill.sttp.model._
import scala.concurrent.Future
-class AkkaHttpSttpHandler(actorSystem: ActorSystem) extends SttpStreamHandler[Future, Source[ByteString, Any]] {
+class AkkaHttpSttpHandler(actorSystem: ActorSystem)
+ extends SttpHandler[Future, Source[ByteString, Any], ResponseAs] {
+
def this() = this(ActorSystem("sttp"))
private implicit val as = actorSystem
private implicit val materializer = ActorMaterializer()
import as.dispatcher
- override def send[T](r: Request, responseAs: ResponseAs[T]): Future[Response[T]] = {
+ override def send[T](r: Request, responseAs: ResponseAs[T, Source[ByteString, Any]]): Future[Response[T]] = {
requestToAkka(r).flatMap(setBodyOnAkka(r, r.body, _)).flatMap(Http().singleRequest(_)).flatMap { hr =>
val code = hr.status.intValue()
bodyFromAkka(responseAs, hr).map(Response(code, _))
}
}
- override def send(r: Request, responseAsStream: ResponseAsStream[Source[ByteString, Any]]): Future[Response[Source[ByteString, Any]]] = {
- requestToAkka(r).flatMap(setBodyOnAkka(r, r.body, _)).flatMap(Http().singleRequest(_)).map { hr =>
- val code = hr.status.intValue()
- Response(code, hr.entity.dataBytes)
- }
- }
-
private def methodToAkka(m: Method): HttpMethod = m match {
case Method.GET => HttpMethods.GET
case Method.HEAD => HttpMethods.HEAD
@@ -48,7 +43,7 @@ class AkkaHttpSttpHandler(actorSystem: ActorSystem) extends SttpStreamHandler[Fu
case _ => HttpMethod.custom(m.m)
}
- private def bodyFromAkka[T](rr: ResponseAs[T], hr: HttpResponse): Future[T] = {
+ private def bodyFromAkka[T](rr: ResponseAs[T, Source[ByteString, Any]], hr: HttpResponse): Future[T] = {
def asByteArray = hr.entity.dataBytes
.runFold(ByteString(""))(_ ++ _)
.map(_.toArray[Byte])
@@ -63,6 +58,9 @@ class AkkaHttpSttpHandler(actorSystem: ActorSystem) extends SttpStreamHandler[Fu
case ResponseAsByteArray =>
asByteArray
+
+ case r @ ResponseAsStream() =>
+ Future.successful(r.x(hr.entity.dataBytes))
}
}
diff --git a/core/src/main/scala/com/softwaremill/sttp/HttpConnectionSttpHandler.scala b/core/src/main/scala/com/softwaremill/sttp/HttpConnectionSttpHandler.scala
index 362bdab..f7292d7 100644
--- a/core/src/main/scala/com/softwaremill/sttp/HttpConnectionSttpHandler.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/HttpConnectionSttpHandler.scala
@@ -10,8 +10,8 @@ import com.softwaremill.sttp.model._
import scala.annotation.tailrec
import scala.io.Source
-object HttpConnectionSttpHandler extends SttpHandler[Id] {
- override def send[T](r: Request, responseAs: ResponseAs[T]): Response[T] = {
+object HttpConnectionSttpHandler extends SttpHandler[Id, Any, ResponseAsBasic] {
+ override def send[T](r: Request, responseAs: ResponseAsBasic[T, Any]): Response[T] = {
val c = r.uri.toURL.openConnection().asInstanceOf[HttpURLConnection]
c.setRequestMethod(r.method.m)
r.headers.foreach { case (k, v) => c.setRequestProperty(k, v) }
@@ -68,7 +68,7 @@ object HttpConnectionSttpHandler extends SttpHandler[Id] {
}
}
- private def readResponse[T](is: InputStream, responseAs: ResponseAs[T]): T = responseAs match {
+ private def readResponse[T](is: InputStream, responseAs: ResponseAsBasic[T, Any]): T = responseAs match {
case IgnoreResponse =>
@tailrec def consume(): Unit = if (is.read() != -1) consume()
consume()
diff --git a/core/src/main/scala/com/softwaremill/sttp/SttpHandler.scala b/core/src/main/scala/com/softwaremill/sttp/SttpHandler.scala
index 05c7199..50ccd35 100644
--- a/core/src/main/scala/com/softwaremill/sttp/SttpHandler.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/SttpHandler.scala
@@ -4,10 +4,33 @@ import com.softwaremill.sttp.model.{ResponseAs, ResponseAsStream}
import scala.language.higherKinds
-trait SttpHandler[R[_]] {
- def send[T](request: Request, responseAs: ResponseAs[T]): R[Response[T]]
+trait SttpHandler[R[_], +S, -AcceptsResponseAs[x, -s] <: ResponseAs[x, s]] {
+ def send[T](request: Request, responseAs: AcceptsResponseAs[T, S]): R[Response[T]]
}
-trait SttpStreamHandler[R[_], S] extends SttpHandler[R] {
- def send(request: Request, responseAsStream: ResponseAsStream[S]): R[Response[S]]
-} \ No newline at end of file
+//trait SttpStreamHandler[R[_], S] extends SttpHandler[R] {
+// def send(request: Request, responseAsStream: ResponseAsStream[S]): R[Response[S]]
+//}
+
+
+/*
+
+Cat <: Animal
+Dog <: Animal
+
+x: Animal := Cat
+
+Contravariant:
+def eat(x: Cat) := def eat(x: Animal)
+Animal => Cat := Animal => Unit
+
+Covariant:
+def create: Animal := def create: Cat
+Unit => Animal := Unit => Cat
+
+---
+
+RA >: RAS
+Handler[RAS] >: Handler[RA]
+
+ */ \ No newline at end of file
diff --git a/core/src/main/scala/com/softwaremill/sttp/model/package.scala b/core/src/main/scala/com/softwaremill/sttp/model/package.scala
index e50426c..3c0e563 100644
--- a/core/src/main/scala/com/softwaremill/sttp/model/package.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/model/package.scala
@@ -4,6 +4,8 @@ import java.io.{File, InputStream}
import java.nio.ByteBuffer
import java.nio.file.Path
+import scala.language.higherKinds
+
package object model {
case class Method(m: String) extends AnyVal
object Method {
@@ -36,11 +38,13 @@ package object model {
case class FileBody(f: File) extends BasicRequestBody
case class PathBody(f: Path) extends BasicRequestBody
- sealed trait ResponseAs[T]
- object IgnoreResponse extends ResponseAs[Unit]
- case class ResponseAsString(encoding: String) extends ResponseAs[String]
- object ResponseAsByteArray extends ResponseAs[Array[Byte]]
+ sealed trait ResponseAs[T, -S]
+
+ sealed trait ResponseAsBasic[T, -S <: Any] extends ResponseAs[T, S]
+ object IgnoreResponse extends ResponseAsBasic[Unit, Any]
+ case class ResponseAsString(encoding: String) extends ResponseAsBasic[String, Any]
+ object ResponseAsByteArray extends ResponseAsBasic[Array[Byte], Any]
// response as params
- case class ResponseAsStream[S]()
+ case class ResponseAsStream[T, S]()(implicit val x: S =:= T) extends ResponseAs[T, S]
}
diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala
index 40b8d82..e4f5311 100644
--- a/core/src/main/scala/com/softwaremill/sttp/package.scala
+++ b/core/src/main/scala/com/softwaremill/sttp/package.scala
@@ -12,62 +12,17 @@ import scala.language.higherKinds
import scala.collection.immutable.Seq
package object sttp {
- /*
-
- - set headers
- - set cookies (set from response)
- - partial request (no uri + method) / full request
- - start with an empty partial request
- - multi-part uploads
- - body: bytes, input stream (?), task/future, stream (fs2/akka), form data, file
- - auth
- - access uri/method/headers/cookies/body spec
- - proxy
- - user agent, buffer size
- - charset
- - zipped encodings
- - SSL - mutual? (client side)
-
- - stream responses (sendStreamAndReceive?) / strict responses
- - make sure response is consumed - only fire request when we know what to do with response?
-
- - reuse connections / connection pooling - in handler
-
- - handler restriction? AnyHandler <: Handler Restriction
-
- Options:
- - timeouts (connection/read)
- - follow redirect
- - ignore SSL
-
- //
-
- We want to serialize to:
- - string
- - byte array
- - input stream
- - handler-specific stream of bytes/strings
-
- post:
- - data (bytes/is/string - but which encoding?)
- - form data (kv pairs - application/x-www-form-urlencoded)
- - multipart (files mixed with forms - multipart/form-data)
-
- */
-
- //
-
type Id[X] = X
type Empty[X] = None.type
- def ignoreResponse: ResponseAs[Unit] = IgnoreResponse
+ def ignoreResponse: ResponseAsBasic[Unit, Any] = IgnoreResponse
/**
* Uses `utf-8` encoding.
*/
- def responseAsString: ResponseAs[String] = responseAsString(Utf8)
- def responseAsString(encoding: String): ResponseAs[String] = ResponseAsString(encoding)
- def responseAsByteArray: ResponseAs[Array[Byte]] = ResponseAsByteArray
- def responseAsStream[S]: ResponseAsStream[S] = ResponseAsStream[S]()
+ def responseAsString: ResponseAsBasic[String, Any] = responseAsString(Utf8)
+ def responseAsString(encoding: String): ResponseAsBasic[String, Any] = ResponseAsString(encoding)
+ def responseAsByteArray: ResponseAsBasic[Array[Byte], Any] = ResponseAsByteArray
+ def responseAsStream[S]: ResponseAs[S, S] = ResponseAsStream[S, S]()
/**
* Use the factory methods `multiPart` to conveniently create instances of this class. A part can be then
@@ -189,15 +144,14 @@ package object sttp {
def multipartData(parts: MultiPart*): RequestTemplate[U] = ???
- def send[R[_], T](responseAs: ResponseAs[T])(
- implicit handler: SttpHandler[R], isRequest: IsRequest[U]): R[Response[T]] = {
-
- handler.send(this, responseAs)
- }
-
- def send[R[_], S](responseAs: ResponseAsStream[S])(
- implicit handler: SttpStreamHandler[R, S], isRequest: IsRequest[U]): R[Response[S]] = {
-
+ /**
+ * @param responseAs What's the target type to which the response should be read. Needs to be specified upfront
+ * so that the response is always consumed and hence there are no requirements on client code
+ * to consume it. An exception to this are streaming responses, which need to fully consumed
+ * by the client if such a response type is requested.
+ */
+ def send[R[_], S, T, ResponseAsType[x, -s] <: ResponseAs[x, s]](responseAs: ResponseAsType[T, S])(
+ implicit handler: SttpHandler[R, S, ResponseAsType], isRequest: IsRequest[U]): R[Response[T]] = {
handler.send(this, responseAs)
}
}