diff options
author | adamw <adam@warski.org> | 2017-07-24 17:39:54 +0200 |
---|---|---|
committer | adamw <adam@warski.org> | 2017-07-24 17:39:54 +0200 |
commit | 0299feaebb4b72bbcfc62af23ced1f297cf26294 (patch) | |
tree | e44429a97022d15b3b6231e6192e73c9b1f4779a /core | |
parent | a568123e2a7959b860faaedb66b358fabd92e317 (diff) | |
download | sttp-0299feaebb4b72bbcfc62af23ced1f297cf26294.tar.gz sttp-0299feaebb4b72bbcfc62af23ced1f297cf26294.tar.bz2 sttp-0299feaebb4b72bbcfc62af23ced1f297cf26294.zip |
Splitting the package object into smaller classes
Diffstat (limited to 'core')
3 files changed, 340 insertions, 312 deletions
diff --git a/core/src/main/scala/com/softwaremill/sttp/MultiPart.scala b/core/src/main/scala/com/softwaremill/sttp/MultiPart.scala new file mode 100644 index 0000000..eb7badb --- /dev/null +++ b/core/src/main/scala/com/softwaremill/sttp/MultiPart.scala @@ -0,0 +1,19 @@ +package com.softwaremill.sttp + +import com.softwaremill.sttp.model.BasicRequestBody + +/** + * Use the factory methods `multiPart` to conveniently create instances of + * this class. A part can be then further customised using `fileName`, + * `contentType` and `header` methods. + */ +case class MultiPart(name: String, + data: BasicRequestBody, + fileName: Option[String] = None, + contentType: Option[String] = None, + additionalHeaders: Map[String, String] = Map()) { + def fileName(v: String): MultiPart = copy(fileName = Some(v)) + def contentType(v: String): MultiPart = copy(contentType = Some(v)) + def header(k: String, v: String): MultiPart = + copy(additionalHeaders = additionalHeaders + (k -> v)) +} diff --git a/core/src/main/scala/com/softwaremill/sttp/RequestT.scala b/core/src/main/scala/com/softwaremill/sttp/RequestT.scala new file mode 100644 index 0000000..be257dd --- /dev/null +++ b/core/src/main/scala/com/softwaremill/sttp/RequestT.scala @@ -0,0 +1,269 @@ +package com.softwaremill.sttp + +import java.io.{File, InputStream} +import java.net.{URI, URLEncoder} +import java.nio.ByteBuffer +import java.nio.file.Path +import java.util.Base64 + +import com.softwaremill.sttp.model._ + +import scala.collection.immutable.Seq + +/** + * @tparam U Specifies if the method & uri are specified. By default can be + * either: + * * `Empty`, which is a type constructor which always resolves to + * `None`. This type of request is aliased to `PartialRequest`: + * there's no method and uri specified, and the request cannot be + * sent. + * * `Id`, which is an identity type constructor. This type of + * request is aliased to `Request`: the method and uri are + * specified, and the request can be sent. + */ +case class RequestT[U[_], T, +S]( + method: U[Method], + uri: U[URI], + body: RequestBody[S], + headers: Seq[(String, String)], + responseAs: ResponseAs[T, S] +) { + def get(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.GET) + def head(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.HEAD) + def post(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.POST) + def put(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.PUT) + def delete(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.DELETE) + def options(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.OPTIONS) + def patch(uri: URI): Request[T, S] = + this.copy[Id, T, S](uri = uri, method = Method.PATCH) + + def contentType(ct: String): RequestT[U, T, S] = + header(ContentTypeHeader, ct, replaceExisting = true) + def contentType(ct: String, encoding: String): RequestT[U, T, S] = + header(ContentTypeHeader, + contentTypeWithEncoding(ct, encoding), + replaceExisting = true) + def contentLength(l: Long): RequestT[U, T, S] = + header(ContentLengthHeader, l.toString, replaceExisting = true) + def header(k: String, + v: String, + replaceExisting: Boolean = false): RequestT[U, T, S] = { + val current = + if (replaceExisting) + headers.filterNot(_._1.equalsIgnoreCase(k)) + else headers + this.copy(headers = current :+ (k -> v)) + } + def headers(hs: Map[String, String]): RequestT[U, T, S] = + this.copy(headers = headers ++ hs.toSeq) + def headers(hs: (String, String)*): RequestT[U, T, S] = + this.copy(headers = headers ++ hs) + def cookie(nv: (String, String)): RequestT[U, T, S] = cookies(nv) + def cookie(n: String, v: String): RequestT[U, T, S] = cookies((n, v)) + def cookies(r: Response[_]): RequestT[U, T, S] = + cookies(r.cookies.map(c => (c.name, c.value)): _*) + def cookies(cs: Seq[Cookie]): RequestT[U, T, S] = + cookies(cs.map(c => (c.name, c.value)): _*) + def cookies(nvs: (String, String)*): RequestT[U, T, S] = + header(CookieHeader, nvs.map(p => p._1 + "=" + p._2).mkString("; ")) + def auth: SpecifyAuthScheme[U, T, S] = + new SpecifyAuthScheme[U, T, S](AuthorizationHeader, this) + def proxyAuth: SpecifyAuthScheme[U, T, S] = + new SpecifyAuthScheme[U, T, S](ProxyAuthorizationHeader, this) + def acceptEncoding(encoding: String): RequestT[U, T, S] = + header(AcceptEncodingHeader, encoding) + + /** + * Uses the `utf-8` encoding. + * + * If content type is not yet specified, will be set to `text/plain` + * with `utf-8` encoding. + * + * If content length is not yet specified, will be set to the number of + * bytes in the string using the `utf-8` encoding. + */ + def body(b: String): RequestT[U, T, S] = body(b, Utf8) + + /** + * If content type is not yet specified, will be set to `text/plain` + * with the given encoding. + * + * If content length is not yet specified, will be set to the number of + * bytes in the string using the given encoding. + */ + def body(b: String, encoding: String): RequestT[U, T, S] = + setContentTypeIfMissing( + contentTypeWithEncoding(TextPlainContentType, encoding)) + .setContentLengthIfMissing(b.getBytes(encoding).length) + .copy(body = StringBody(b, encoding)) + + /** + * If content type is not yet specified, will be set to + * `application/octet-stream`. + * + * If content length is not yet specified, will be set to the length + * of the given array. + */ + def body(b: Array[Byte]): RequestT[U, T, S] = + setContentTypeIfMissing(ApplicationOctetStreamContentType) + .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)) + + /** + * 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)) + + /** + * If content type is not yet specified, will be set to + * `application/octet-stream`. + * + * If content length is not yet specified, will be set to the length + * of the given file. + */ + def body(b: File): RequestT[U, T, S] = + body(b.toPath) + + /** + * If content type is not yet specified, will be set to + * `application/octet-stream`. + * + * If content length is not yet specified, will be set to the length + * of the given file. + */ + def body(b: Path): RequestT[U, T, S] = + setContentTypeIfMissing(ApplicationOctetStreamContentType) + .setContentLengthIfMissing(b.toFile.length()) + .copy(body = PathBody(b)) + + /** + * Encodes the given parameters as form data using `utf-8`. + * If content type is not yet specified, will be set to + * `application/x-www-form-urlencoded`. + * + * If content length is not yet specified, will be set to the length + * of the number of bytes in the url-encoded parameter string. + */ + def body(fs: Map[String, String]): RequestT[U, T, S] = + formDataBody(fs.toList, Utf8) + + /** + * Encodes the given parameters as form data. + * If content type is not yet specified, will be set to + * `application/x-www-form-urlencoded`. + * + * If content length is not yet specified, will be set to the length + * of the number of bytes in the url-encoded parameter string. + */ + def body(fs: Map[String, String], encoding: String): RequestT[U, T, S] = + formDataBody(fs.toList, encoding) + + /** + * Encodes the given parameters as form data using `utf-8`. + * If content type is not yet specified, will be set to + * `application/x-www-form-urlencoded`. + * + * If content length is not yet specified, will be set to the length + * of the number of bytes in the url-encoded parameter string. + */ + def body(fs: (String, String)*): RequestT[U, T, S] = + formDataBody(fs.toList, Utf8) + + /** + * Encodes the given parameters as form data. + * If content type is not yet specified, will be set to + * `application/x-www-form-urlencoded`. + * + * If content length is not yet specified, will be set to the length + * of the number of bytes in the url-encoded parameter string. + */ + def body(fs: Seq[(String, String)], encoding: String): RequestT[U, T, S] = + formDataBody(fs, encoding) + + /** + * If content type is not yet specified, will be set to + * `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] = ??? + + def streamBody[S2 >: S](b: S2): RequestT[U, T, S2] = + this.copy[U, T, S2](body = StreamBody(b)) + + /** + * What's the target type to which the response body 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 response[T2, S2 >: S](ra: ResponseAs[T2, S2]): RequestT[U, T2, S2] = + this.copy(responseAs = ra) + + def mapResponse[T2](f: T => T2): RequestT[U, T2, S] = + this.copy(responseAs = responseAs.map(f)) + + def send[R[_]]()(implicit handler: SttpHandler[R, S], + isIdInRequest: IsIdInRequest[U]): R[Response[T]] = { + // we could avoid the asInstanceOf by creating an artificial copy + // changing the method & url fields using `isIdInRequest`, but that + // would be only to satisfy the type checker, and a needless copy at + // runtime. + handler.send(this.asInstanceOf[RequestT[Id, T, S]]) + } + + private def hasContentType: Boolean = + headers.exists(_._1.equalsIgnoreCase(ContentTypeHeader)) + private def setContentTypeIfMissing(ct: String): RequestT[U, T, S] = + if (hasContentType) this else contentType(ct) + + private def hasContentLength: Boolean = + headers.exists(_._1.equalsIgnoreCase(ContentLengthHeader)) + private def setContentLengthIfMissing(l: => Long): RequestT[U, T, S] = + if (hasContentLength) this else contentLength(l) + + 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)) + .mkString("&") + setContentTypeIfMissing(ApplicationFormContentType) + .setContentLengthIfMissing(b.getBytes(encoding).length) + .copy(body = StringBody(b, encoding)) + } +} + +class SpecifyAuthScheme[U[_], T, +S](hn: String, rt: RequestT[U, T, S]) { + def basic(user: String, password: String): RequestT[U, T, S] = { + val c = new String( + Base64.getEncoder.encode(s"$user:$password".getBytes(Utf8)), + Utf8) + rt.header(hn, s"Basic $c") + } + + def bearer(token: String): RequestT[U, T, S] = + rt.header(hn, s"Bearer $token") +} diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala index f55ed9e..ccc79ac 100644 --- a/core/src/main/scala/com/softwaremill/sttp/package.scala +++ b/core/src/main/scala/com/softwaremill/sttp/package.scala @@ -16,6 +16,53 @@ package object sttp { type Id[X] = X type Empty[X] = None.type + type PartialRequest[T, +S] = RequestT[Empty, T, S] + type Request[T, +S] = RequestT[Id, T, S] + + @implicitNotFound( + "This is a partial request, the method & url are not specified. Use " + + ".get(...), .post(...) etc. to obtain a non-partial request.") + private[sttp] type IsIdInRequest[U[_]] = U[Unit] =:= Id[Unit] + + // constants + + private[sttp] val ContentTypeHeader = "Content-Type" + private[sttp] val ContentLengthHeader = "Content-Length" + private[sttp] val SetCookieHeader = "Set-Cookie" + private[sttp] val CookieHeader = "Cookie" + private[sttp] val AuthorizationHeader = "Authorization" + private[sttp] val ProxyAuthorizationHeader = "Proxy-Authorization" + private[sttp] val AcceptEncodingHeader = "Accept-Encoding" + private[sttp] val ContentEncodingHeader = "Content-Encoding" + private[sttp] val Utf8 = "utf-8" + + private[sttp] val ApplicationOctetStreamContentType = + "application/octet-stream" + private[sttp] val ApplicationFormContentType = + "application/x-www-form-urlencoded" + private[sttp] val TextPlainContentType = "text/plain" + private[sttp] val MultipartFormDataContentType = "multipart/form-data" + + // entry points + + /** + * An empty request with no headers. + */ + val emptyRequest: RequestT[Empty, String, Nothing] = + RequestT[Empty, String, Nothing](None, None, NoBody, Vector(), asString) + + /** + * A starting request, with the following modifications comparing to + * `emptyRequest`: + * + * - `Accept-Encoding` set to `gzip, deflate` (handled automatically by the + * library) + */ + val sttp: RequestT[Empty, String, Nothing] = + emptyRequest.acceptEncoding("gzip, deflate") + + // response specifications + def ignore: ResponseAs[Unit, Nothing] = IgnoreResponse /** @@ -37,21 +84,7 @@ package object sttp { def asStream[S]: ResponseAs[S, S] = ResponseAsStream[S, S]() - /** - * Use the factory methods `multiPart` to conveniently create instances of - * this class. A part can be then further customised using `fileName`, - * `contentType` and `header` methods. - */ - case class MultiPart(name: String, - data: BasicRequestBody, - fileName: Option[String] = None, - contentType: Option[String] = None, - additionalHeaders: Map[String, String] = Map()) { - def fileName(v: String): MultiPart = copy(fileName = Some(v)) - def contentType(v: String): MultiPart = copy(contentType = Some(v)) - def header(k: String, v: String): MultiPart = - copy(additionalHeaders = additionalHeaders + (k -> v)) - } + // multi part factory methods /** * Content type will be set to `text/plain` with `utf-8` encoding, can be @@ -117,306 +150,13 @@ package object sttp { fileName = Some(data.getFileName.toString), contentType = Some(ApplicationOctetStreamContentType)) - /** - * @tparam U Specifies if the method & uri are specified. By default can be - * either: - * * `Empty`, which is a type constructor which always resolves to - * `None`. This type of request is aliased to `PartialRequest`: - * there's no method and uri specified, and the request cannot be - * sent. - * * `Id`, which is an identity type constructor. This type of - * request is aliased to `Request`: the method and uri are - * specified, and the request can be sent. - */ - case class RequestT[U[_], T, +S]( - method: U[Method], - uri: U[URI], - body: RequestBody[S], - headers: Seq[(String, String)], - responseAs: ResponseAs[T, S] - ) { - def get(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.GET) - def head(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.HEAD) - def post(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.POST) - def put(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.PUT) - def delete(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.DELETE) - def options(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.OPTIONS) - def patch(uri: URI): Request[T, S] = - this.copy[Id, T, S](uri = uri, method = Method.PATCH) - - def contentType(ct: String): RequestT[U, T, S] = - header(ContentTypeHeader, ct, replaceExisting = true) - def contentType(ct: String, encoding: String): RequestT[U, T, S] = - header(ContentTypeHeader, - contentTypeWithEncoding(ct, encoding), - replaceExisting = true) - def contentLength(l: Long): RequestT[U, T, S] = - header(ContentLengthHeader, l.toString, replaceExisting = true) - def header(k: String, - v: String, - replaceExisting: Boolean = false): RequestT[U, T, S] = { - val current = - if (replaceExisting) - headers.filterNot(_._1.equalsIgnoreCase(k)) - else headers - this.copy(headers = current :+ (k -> v)) - } - def headers(hs: Map[String, String]): RequestT[U, T, S] = - this.copy(headers = headers ++ hs.toSeq) - def headers(hs: (String, String)*): RequestT[U, T, S] = - this.copy(headers = headers ++ hs) - def cookie(nv: (String, String)): RequestT[U, T, S] = cookies(nv) - def cookie(n: String, v: String): RequestT[U, T, S] = cookies((n, v)) - def cookies(r: Response[_]): RequestT[U, T, S] = - cookies(r.cookies.map(c => (c.name, c.value)): _*) - def cookies(cs: Seq[Cookie]): RequestT[U, T, S] = - cookies(cs.map(c => (c.name, c.value)): _*) - def cookies(nvs: (String, String)*): RequestT[U, T, S] = - header(CookieHeader, nvs.map(p => p._1 + "=" + p._2).mkString("; ")) - def auth: SpecifyAuthScheme[U, T, S] = - new SpecifyAuthScheme[U, T, S](AuthorizationHeader, this) - def proxyAuth: SpecifyAuthScheme[U, T, S] = - new SpecifyAuthScheme[U, T, S](ProxyAuthorizationHeader, this) - def acceptEncoding(encoding: String): RequestT[U, T, S] = - header(AcceptEncodingHeader, encoding) - - /** - * Uses the `utf-8` encoding. - * - * If content type is not yet specified, will be set to `text/plain` - * with `utf-8` encoding. - * - * If content length is not yet specified, will be set to the number of - * bytes in the string using the `utf-8` encoding. - */ - def body(b: String): RequestT[U, T, S] = body(b, Utf8) - - /** - * If content type is not yet specified, will be set to `text/plain` - * with the given encoding. - * - * If content length is not yet specified, will be set to the number of - * bytes in the string using the given encoding. - */ - def body(b: String, encoding: String): RequestT[U, T, S] = - setContentTypeIfMissing( - contentTypeWithEncoding(TextPlainContentType, encoding)) - .setContentLengthIfMissing(b.getBytes(encoding).length) - .copy(body = StringBody(b, encoding)) - - /** - * If content type is not yet specified, will be set to - * `application/octet-stream`. - * - * If content length is not yet specified, will be set to the length - * of the given array. - */ - def body(b: Array[Byte]): RequestT[U, T, S] = - setContentTypeIfMissing(ApplicationOctetStreamContentType) - .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)) - - /** - * 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)) - - /** - * If content type is not yet specified, will be set to - * `application/octet-stream`. - * - * If content length is not yet specified, will be set to the length - * of the given file. - */ - def body(b: File): RequestT[U, T, S] = - body(b.toPath) - - /** - * If content type is not yet specified, will be set to - * `application/octet-stream`. - * - * If content length is not yet specified, will be set to the length - * of the given file. - */ - def body(b: Path): RequestT[U, T, S] = - setContentTypeIfMissing(ApplicationOctetStreamContentType) - .setContentLengthIfMissing(b.toFile.length()) - .copy(body = PathBody(b)) - - /** - * Encodes the given parameters as form data using `utf-8`. - * If content type is not yet specified, will be set to - * `application/x-www-form-urlencoded`. - * - * If content length is not yet specified, will be set to the length - * of the number of bytes in the url-encoded parameter string. - */ - def body(fs: Map[String, String]): RequestT[U, T, S] = - formDataBody(fs.toList, Utf8) + // util - /** - * Encodes the given parameters as form data. - * If content type is not yet specified, will be set to - * `application/x-www-form-urlencoded`. - * - * If content length is not yet specified, will be set to the length - * of the number of bytes in the url-encoded parameter string. - */ - def body(fs: Map[String, String], encoding: String): RequestT[U, T, S] = - formDataBody(fs.toList, encoding) - - /** - * Encodes the given parameters as form data using `utf-8`. - * If content type is not yet specified, will be set to - * `application/x-www-form-urlencoded`. - * - * If content length is not yet specified, will be set to the length - * of the number of bytes in the url-encoded parameter string. - */ - def body(fs: (String, String)*): RequestT[U, T, S] = - formDataBody(fs.toList, Utf8) - - /** - * Encodes the given parameters as form data. - * If content type is not yet specified, will be set to - * `application/x-www-form-urlencoded`. - * - * If content length is not yet specified, will be set to the length - * of the number of bytes in the url-encoded parameter string. - */ - def body(fs: Seq[(String, String)], encoding: String): RequestT[U, T, S] = - formDataBody(fs, encoding) - - /** - * If content type is not yet specified, will be set to - * `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] = ??? - - def streamBody[S2 >: S](b: S2): RequestT[U, T, S2] = - this.copy[U, T, S2](body = StreamBody(b)) - - /** - * What's the target type to which the response body 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 response[T2, S2 >: S](ra: ResponseAs[T2, S2]): RequestT[U, T2, S2] = - this.copy(responseAs = ra) - - def mapResponse[T2](f: T => T2): RequestT[U, T2, S] = - this.copy(responseAs = responseAs.map(f)) - - def send[R[_]]()(implicit handler: SttpHandler[R, S], - isIdInRequest: IsIdInRequest[U]): R[Response[T]] = { - // we could avoid the asInstanceOf by creating an artificial copy - // changing the method & url fields using `isIdInRequest`, but that - // would be only to satisfy the type checker, and a needless copy at - // runtime. - handler.send(this.asInstanceOf[RequestT[Id, T, S]]) - } - - private def hasContentType: Boolean = - headers.exists(_._1.equalsIgnoreCase(ContentTypeHeader)) - private def setContentTypeIfMissing(ct: String): RequestT[U, T, S] = - if (hasContentType) this else contentType(ct) - - private def hasContentLength: Boolean = - headers.exists(_._1.equalsIgnoreCase(ContentLengthHeader)) - private def setContentLengthIfMissing(l: => Long): RequestT[U, T, S] = - if (hasContentLength) this else contentLength(l) - - 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)) - .mkString("&") - setContentTypeIfMissing(ApplicationFormContentType) - .setContentLengthIfMissing(b.getBytes(encoding).length) - .copy(body = StringBody(b, encoding)) - } - } - - class SpecifyAuthScheme[U[_], T, +S](hn: String, rt: RequestT[U, T, S]) { - def basic(user: String, password: String): RequestT[U, T, S] = { - val c = new String( - Base64.getEncoder.encode(s"$user:$password".getBytes(Utf8)), - Utf8) - rt.header(hn, s"Basic $c") - } - - def bearer(token: String): RequestT[U, T, S] = - rt.header(hn, s"Bearer $token") - } - - type PartialRequest[T, +S] = RequestT[Empty, T, S] - type Request[T, +S] = RequestT[Id, T, S] - - @implicitNotFound( - "This is a partial request, the method & url are not specified. Use " + - ".get(...), .post(...) etc. to obtain a non-partial request.") - private type IsIdInRequest[U[_]] = U[Unit] =:= Id[Unit] - - private[sttp] val ContentTypeHeader = "Content-Type" - private[sttp] val ContentLengthHeader = "Content-Length" - private[sttp] val SetCookieHeader = "Set-Cookie" - private[sttp] val CookieHeader = "Cookie" - private[sttp] val AuthorizationHeader = "Authorization" - private[sttp] val ProxyAuthorizationHeader = "Proxy-Authorization" - private[sttp] val AcceptEncodingHeader = "Accept-Encoding" - private[sttp] val ContentEncodingHeader = "Content-Encoding" - private val Utf8 = "utf-8" - - private val ApplicationOctetStreamContentType = "application/octet-stream" - private val ApplicationFormContentType = "application/x-www-form-urlencoded" - private val TextPlainContentType = "text/plain" - private val MultipartFormDataContentType = "multipart/form-data" - - /** - * An empty request with no headers. - */ - val emptyRequest: RequestT[Empty, String, Nothing] = - RequestT[Empty, String, Nothing](None, None, NoBody, Vector(), asString) - - /** - * A starting request, with the following modifications comparing to - * `emptyRequest`: - * - * - `Accept-Encoding` set to `gzip, deflate` (handled automatically by the - * library) - */ - val sttp: RequestT[Empty, String, Nothing] = - emptyRequest.acceptEncoding("gzip, deflate") - - private def contentTypeWithEncoding(ct: String, enc: String) = + private[sttp] def contentTypeWithEncoding(ct: String, enc: String) = s"$ct; charset=$enc" + // uri interpolator + implicit class UriContext(val sc: StringContext) extends AnyVal { def uri(args: Any*): URI = UriInterpolator.interpolate(sc, args: _*) } |