Skip to content

Instantly share code, notes, and snippets.

@thecodeassassin
Created November 10, 2016 09:24
Show Gist options
  • Save thecodeassassin/cafb37217b54ef1bbb4142461a63f0db to your computer and use it in GitHub Desktop.
Save thecodeassassin/cafb37217b54ef1bbb4142461a63f0db to your computer and use it in GitHub Desktop.
def httpWithEntity[A](method: HttpMethod, uri: String, body: Option[RequestEntity], headers: List[(String, String)] = jsonHeaders, logError: Boolean = true)(implicit mf: scala.reflect.Manifest[A]): Future[A] = {
val requestLog = s"[${method.toString} $uri]"
val requestUri = Uri(uri)
val requestHeaders = headers.map { header ⇒ HttpHeader.parse(header._1, header._2) } collect {
case ParsingResult.Ok(h, _) ⇒ h
}
val request = HttpRequest(uri = requestUri.toRelative, method = method, headers = requestHeaders)
val requestWithEntity = body match {
case Some(entity) ⇒
logger.trace(s"req $requestLog - $entity")
request.withEntity(entity)
case None ⇒
logger.trace(s"req $requestLog")
request
}
def recoverWith[T]: PartialFunction[Throwable, T] = {
case exception: HttpClientException ⇒ throw exception
case exception: ExecutionException if exception.getCause != null && exception.getCause.getClass == classOf[HttpClientException] ⇒ throw exception.getCause
case exception ⇒
val message = s"rsp $requestLog - exception: ${exception.getMessage}"
if (logError) {
logger.error(message)
logger.trace(message, exception)
}
throw HttpClientException(None, exception.getMessage).initCause(if (exception.getCause != null) exception.getCause else exception)
}
def decode(entity: ResponseEntity): Future[String] = entity.toStrict(timeout.duration).map(_.data.decodeString("UTF-8"))
Source.single(requestWithEntity)
.via(Http().outgoingConnection(requestUri.authority.host.address, requestUri.authority.port))
.recover(recoverWith)
.mapAsync(1)({
case HttpResponse(status, _, entity, _) ⇒ status.intValue() match {
case code if code / 100 == 2 && (classTag[A].runtimeClass == classOf[Nothing] || classTag[A].runtimeClass == classOf[String]) ⇒
decode(entity).map { body ⇒
logger.trace(s"rsp $requestLog - $body")
body.asInstanceOf[A]
}
case code if code / 100 == 2 ⇒
decode(entity).map({ body ⇒
val json = Try(parse(StringInput(body), useBigDecimalForDouble = true)).getOrElse(JString(body))
Try(logger.trace(s"rsp $requestLog - ${Serialization.writePretty(json)}"))
json.extract[A](formats, mf)
})
case code ⇒
decode(entity).map { body ⇒
val message = s"rsp $requestLog - unexpected status code: $code"
if (logError) logger.error(message)
logger.trace(s"$message, for response: $body")
throw HttpClientException(Some(code), body)
}
}
case other: AnyRef ⇒ throw new RuntimeException(other.toString)
}).runWith(Sink.head)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment