1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
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}
import scala.collection.immutable.Seq
import scala.language.higherKinds
import scala.util.Try
/**
* @tparam T Target type as which the response will be read.
* @tparam S If `T` is a stream, the type of the stream. Otherwise, `Nothing`.
*/
sealed trait ResponseAs[T, +S] {
def map[T2](f: T => T2): ResponseAs[T2, S]
}
/**
* Response handling specification which isn't derived from another response
* handling method, but needs to be handled directly by the backend.
*/
sealed trait BasicResponseAs[T, +S] extends ResponseAs[T, S] {
override def map[T2](f: (T) => T2): ResponseAs[T2, S] =
MappedResponseAs[T, T2, S](this, f)
}
case object IgnoreResponse extends BasicResponseAs[Unit, Nothing]
case class ResponseAsString(encoding: String)
extends BasicResponseAs[String, Nothing]
case object ResponseAsByteArray extends BasicResponseAs[Array[Byte], Nothing]
case class ResponseAsStream[T, S]()(implicit val responseIsStream: S =:= T)
extends BasicResponseAs[T, S]
case class MappedResponseAs[T, T2, S](raw: BasicResponseAs[T, S], g: T => T2)
extends ResponseAs[T2, S] {
override def map[T3](f: T2 => T3): ResponseAs[T3, S] =
MappedResponseAs[T, T3, S](raw, g andThen f)
}
case class ResponseAsFile(input: File, overwrite: Boolean)
extends BasicResponseAs[File, Nothing]
object ResponseAs {
private[sttp] def parseParams(s: String,
encoding: String): Seq[(String, String)] = {
s.split("&")
.toList
.flatMap(kv =>
kv.split("=", 2) match {
case Array(k, v) =>
Some(
(URLDecoder.decode(k, encoding), URLDecoder.decode(v, encoding)))
case _ => None
})
}
private[sttp] def saveFile(file: File,
is: InputStream,
overwrite: Boolean): File = {
if (!file.exists()) {
file.getParentFile.mkdirs()
file.createNewFile()
} else if (!overwrite) {
throw new IOException(
s"File ${file.getAbsolutePath} exists - overwriting prohibited")
}
val os = new FileOutputStream(file)
transfer(is, os)
file
}
/**
* Handles responses according to the given specification when basic
* response specifications can be handled eagerly, that is without
* wrapping the result in the target monad (`handleBasic` returns
* `Try[T]`, not `R[T]`).
*/
private[sttp] trait EagerResponseHandler[S] {
def handleBasic[T](bra: BasicResponseAs[T, S]): Try[T]
def handle[T, R[_]](responseAs: ResponseAs[T, S],
responseMonad: MonadError[R]): R[T] = {
responseAs match {
case mra @ MappedResponseAs(raw, g) =>
responseMonad.map(responseMonad.fromTry(handleBasic(mra.raw)), mra.g)
case bra: BasicResponseAs[T, S] =>
responseMonad.fromTry(handleBasic(bra))
}
}
}
}
|