From 6a276cfc82c3afd15fc65c6fa369e0eaf6ad5294 Mon Sep 17 00:00:00 2001 From: Omar Alejandro Mainegra Sarduy Date: Fri, 28 Jul 2017 16:18:41 -0400 Subject: First attempt for #13: Created Sync and Future handlers based on OkHttp3. --- .../sttp/okhttp/OkHttpClientHandler.scala | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala (limited to 'okhttp-client-handler/src') 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 new file mode 100644 index 0000000..534bd44 --- /dev/null +++ b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala @@ -0,0 +1,133 @@ +package com.softwaremill.sttp.okhttp + +import java.io.IOException +import java.nio.charset.Charset + +import com.softwaremill.sttp._ +import com.softwaremill.sttp.model._ +import okhttp3.internal.http.HttpMethod +import okhttp3.{ + Call, + Callback, + MediaType, + OkHttpClient, + Request => OkHttpRequest, + RequestBody => OkHttpRequestBody, + Response => OkHttpResponse +} +import okio.{BufferedSink, Okio} + +import scala.collection.JavaConverters._ +import scala.concurrent.{Future, Promise} +import scala.language.higherKinds + +abstract class OkHttpClientHandler[R[_], S](client: OkHttpClient) + extends SttpHandler[R, S] { + private[okhttp] def convertRequest[T]( + request: Request[T, S]): OkHttpRequest = { + val builder = new OkHttpRequest.Builder() + .url(request.uri.toURL) + + val body = setBody(request.body) + builder.method(request.method.m, body.getOrElse { + if (HttpMethod.requiresRequestBody(request.method.m)) + OkHttpRequestBody.create(null, "") + else null + }) + + //OkHttp support automatic gzip compression + request.headers.filter(_._1 != "Accept-Encoding").foreach { + case (name, value) => builder.addHeader(name, value) + } + + builder.build() + } + + private def setBody(requestBody: RequestBody[S]): Option[OkHttpRequestBody] = { + requestBody match { + case NoBody => None + 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) => + 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) => None + } + } + + private[okhttp] def readResponse[T]( + res: OkHttpResponse, + responseAs: ResponseAs[T, S]): Response[T] = { + val body = readResponseBody(res, responseAs) + + val headers = res + .headers() + .names() + .asScala + .flatMap(name => res.headers().values(name).asScala.map((name, _))) + Response(body, res.code(), headers.toList) + } + + private[okhttp] def readResponseBody[T](res: OkHttpResponse, + responseAs: ResponseAs[T, S]): T = { + responseAs match { + case IgnoreResponse => res.body().close() + case ResponseAsString(encoding) => + res.body().source().readString(Charset.forName(encoding)) + case ResponseAsByteArray => res.body().bytes() + case MappedResponseAs(raw, g) => g(readResponseBody(res, raw)) + case r @ ResponseAsParams(enc) => + r.parse(res.body().source().readString(Charset.forName(enc))) + case ResponseAsStream() => throw new IllegalStateException() + } + } +} + +class OkHttpSyncClientHandler(client: OkHttpClient) + extends OkHttpClientHandler[Id, Nothing](client) { + override def send[T](r: Request[T, Nothing]): Response[T] = { + val request = convertRequest(r) + val response = client.newCall(request).execute() + readResponse(response, r.responseAs) + } +} + +object OkHttpSyncClientHandler { + def apply(okhttpClient: OkHttpClient = new OkHttpClient()) + : OkHttpSyncClientHandler = + new OkHttpSyncClientHandler(okhttpClient) +} + +class OkHttpFutureClientHandler(client: OkHttpClient) + extends OkHttpClientHandler[Future, Nothing](client) { + + override def send[T](r: Request[T, Nothing]): Future[Response[T]] = { + val request = convertRequest(r) + val promise = Promise[Response[T]]() + + client + .newCall(request) + .enqueue(new Callback { + override def onFailure(call: Call, e: IOException): Unit = + promise.failure(e) + + override def onResponse(call: Call, response: OkHttpResponse): Unit = + promise.success(readResponse(response, r.responseAs)) + }) + + promise.future + } +} + +object OkHttpFutureClientHandler { + def apply(okhttpClient: OkHttpClient = new OkHttpClient()) + : OkHttpFutureClientHandler = + new OkHttpFutureClientHandler(okhttpClient) +} -- cgit v1.2.3