From 3b7d3085453dea13beb7f5bf125e78a30802ec93 Mon Sep 17 00:00:00 2001 From: Johannes Rudolph Date: Fri, 24 Jul 2015 10:11:56 +0200 Subject: [PATCH] !htp,jso,xml #18064 remove implicit parameters of Unmarshaller instances --- .../docs/http/scaladsl/UnmarshalSpec.scala | 6 +- .../javadsl/marshallers/jackson/Jackson.scala | 5 +- .../sprayjson/SprayJsonSupport.scala | 8 +- .../marshallers/xml/ScalaXmlSupport.scala | 4 +- .../http/javadsl/testkit/TestResponse.scala | 2 +- .../directives/FormFieldDirectivesSpec.scala | 3 + .../http/impl/server/UnmarshallerImpl.scala | 5 +- .../scala/akka/http/impl/server/Util.scala | 3 +- .../http/javadsl/server/RequestVals.scala | 4 +- .../http/javadsl/server/Unmarshallers.scala | 23 ++-- .../http/scaladsl/common/StrictForm.scala | 76 +++++------ .../directives/FormFieldDirectives.scala | 2 + .../directives/MarshallingDirectives.scala | 5 +- .../directives/ParameterDirectives.scala | 3 + .../unmarshalling/GenericUnmarshallers.scala | 4 +- .../PredefinedFromEntityUnmarshallers.scala | 19 ++- .../PredefinedFromStringUnmarshallers.scala | 125 ++++++++++-------- .../scaladsl/unmarshalling/Unmarshal.scala | 4 +- .../scaladsl/unmarshalling/Unmarshaller.scala | 50 ++++--- 19 files changed, 186 insertions(+), 165 deletions(-) diff --git a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala index e71392417b..693460688d 100644 --- a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala +++ b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/UnmarshalSpec.scala @@ -4,16 +4,18 @@ package docs.http.scaladsl +import akka.stream.{ Materializer, ActorMaterializer } import akka.stream.testkit.AkkaSpec class UnmarshalSpec extends AkkaSpec { "use unmarshal" in { import akka.http.scaladsl.unmarshalling.Unmarshal - import system.dispatcher + import system.dispatcher // ExecutionContext + implicit val materializer: Materializer = ActorMaterializer() import scala.concurrent.Await - import scala.concurrent.duration._ // ExecutionContext + import scala.concurrent.duration._ val intFuture = Unmarshal("42").to[Int] val int = Await.result(intFuture, 1.second) // don't block in non-test code! diff --git a/akka-http-marshallers-java/akka-http-jackson/src/main/scala/akka/http/javadsl/marshallers/jackson/Jackson.scala b/akka-http-marshallers-java/akka-http-jackson/src/main/scala/akka/http/javadsl/marshallers/jackson/Jackson.scala index 980a2bb643..d2754e825a 100644 --- a/akka-http-marshallers-java/akka-http-jackson/src/main/scala/akka/http/javadsl/marshallers/jackson/Jackson.scala +++ b/akka-http-marshallers-java/akka-http-jackson/src/main/scala/akka/http/javadsl/marshallers/jackson/Jackson.scala @@ -18,10 +18,7 @@ object Jackson { def json[T <: AnyRef](objectMapper: ObjectMapper): Marshaller[T] = jsonMarshaller(objectMapper).asInstanceOf[Marshaller[T]] def jsonAs[T](clazz: Class[T]): Unmarshaller[T] = jsonAs(objectMapper, clazz) def jsonAs[T](objectMapper: ObjectMapper, clazz: Class[T]): Unmarshaller[T] = - UnmarshallerImpl[T] { (_ec, _materializer) ⇒ - implicit val ec = _ec - implicit val mat = _materializer - + UnmarshallerImpl[T] { unmarshalling.Unmarshaller.messageUnmarshallerFromEntityUnmarshaller { // isn't implicitly inferred for unknown reasons unmarshalling.Unmarshaller.stringUnmarshaller .forContentTypes(`application/json`) diff --git a/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala b/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala index 0bab7db8dd..57ac42d3e8 100644 --- a/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala +++ b/akka-http-marshallers-scala/akka-http-spray-json/src/main/scala/akka/http/scaladsl/marshallers/sprayjson/SprayJsonSupport.scala @@ -16,11 +16,11 @@ import spray.json._ * A trait providing automatic to and from JSON marshalling/unmarshalling using an in-scope *spray-json* protocol. */ trait SprayJsonSupport { - implicit def sprayJsonUnmarshallerConverter[T](reader: RootJsonReader[T])(implicit mat: Materializer): FromEntityUnmarshaller[T] = - sprayJsonUnmarshaller(reader, mat) - implicit def sprayJsonUnmarshaller[T](implicit reader: RootJsonReader[T], mat: Materializer): FromEntityUnmarshaller[T] = + implicit def sprayJsonUnmarshallerConverter[T](reader: RootJsonReader[T]): FromEntityUnmarshaller[T] = + sprayJsonUnmarshaller(reader) + implicit def sprayJsonUnmarshaller[T](implicit reader: RootJsonReader[T]): FromEntityUnmarshaller[T] = sprayJsValueUnmarshaller.map(jsonReader[T].read) - implicit def sprayJsValueUnmarshaller(implicit mat: Materializer): FromEntityUnmarshaller[JsValue] = + implicit def sprayJsValueUnmarshaller: FromEntityUnmarshaller[JsValue] = Unmarshaller.byteStringUnmarshaller.forContentTypes(`application/json`).mapWithCharset { (data, charset) ⇒ val input = if (charset == HttpCharsets.`UTF-8`) ParserInput(data.toArray) diff --git a/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala b/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala index 88a50d2c2c..26a80d589f 100644 --- a/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala +++ b/akka-http-marshallers-scala/akka-http-xml/src/main/scala/akka/http/scaladsl/marshallers/xml/ScalaXmlSupport.scala @@ -21,10 +21,10 @@ trait ScalaXmlSupport { def nodeSeqMarshaller(contentType: ContentType): ToEntityMarshaller[NodeSeq] = Marshaller.StringMarshaller.wrap(contentType)(_.toString()) - implicit def defaultNodeSeqUnmarshaller(implicit fm: Materializer): FromEntityUnmarshaller[NodeSeq] = + implicit def defaultNodeSeqUnmarshaller: FromEntityUnmarshaller[NodeSeq] = nodeSeqUnmarshaller(ScalaXmlSupport.nodeSeqContentTypeRanges: _*) - def nodeSeqUnmarshaller(ranges: ContentTypeRange*)(implicit fm: Materializer): FromEntityUnmarshaller[NodeSeq] = + def nodeSeqUnmarshaller(ranges: ContentTypeRange*): FromEntityUnmarshaller[NodeSeq] = Unmarshaller.byteArrayUnmarshaller.forContentTypes(ranges: _*).mapWithCharset { (bytes, charset) ⇒ if (bytes.length > 0) { val reader = new InputStreamReader(new ByteArrayInputStream(bytes), charset.nioCharset) diff --git a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestResponse.scala b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestResponse.scala index dc976422ad..fcf052844f 100644 --- a/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestResponse.scala +++ b/akka-http-testkit/src/main/scala/akka/http/javadsl/testkit/TestResponse.scala @@ -51,7 +51,7 @@ abstract class TestResponse(_response: HttpResponse, awaitAtMost: FiniteDuration */ def entityAs[T](unmarshaller: Unmarshaller[T]): T = Unmarshal(response) - .to(unmarshaller.asInstanceOf[UnmarshallerImpl[T]].scalaUnmarshaller(ec, materializer), ec) + .to(unmarshaller.asInstanceOf[UnmarshallerImpl[T]].scalaUnmarshaller, ec, materializer) .awaitResult(awaitAtMost) /** diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala index e739248aa3..e0a073b68b 100644 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala +++ b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/FormFieldDirectivesSpec.scala @@ -12,6 +12,9 @@ import akka.http.scaladsl.model._ import MediaTypes._ class FormFieldDirectivesSpec extends RoutingSpec { + // FIXME: unfortunately, it has make a come back, this time it's reproducible ... + import akka.http.scaladsl.server.directives.FormFieldDirectives.FieldMagnet + implicit val nodeSeqUnmarshaller = ScalaXmlSupport.nodeSeqUnmarshaller(`text/xml`, `text/html`, `text/plain`) diff --git a/akka-http/src/main/scala/akka/http/impl/server/UnmarshallerImpl.scala b/akka-http/src/main/scala/akka/http/impl/server/UnmarshallerImpl.scala index 84371ea0a0..bbcbbdd193 100644 --- a/akka-http/src/main/scala/akka/http/impl/server/UnmarshallerImpl.scala +++ b/akka-http/src/main/scala/akka/http/impl/server/UnmarshallerImpl.scala @@ -4,9 +4,7 @@ package akka.http.impl.server -import scala.concurrent.ExecutionContext import scala.reflect.ClassTag -import akka.stream.Materializer import akka.http.javadsl.server.Unmarshaller import akka.http.scaladsl.unmarshalling.FromMessageUnmarshaller @@ -14,6 +12,5 @@ import akka.http.scaladsl.unmarshalling.FromMessageUnmarshaller * INTERNAL API * */ -// FIXME: too lenient visibility, currently used to implement Java marshallers, needs proper API, see #16439 -case class UnmarshallerImpl[T](scalaUnmarshaller: (ExecutionContext, Materializer) ⇒ FromMessageUnmarshaller[T])(implicit val classTag: ClassTag[T]) +private[http] case class UnmarshallerImpl[T](scalaUnmarshaller: FromMessageUnmarshaller[T])(implicit val classTag: ClassTag[T]) extends Unmarshaller[T] diff --git a/akka-http/src/main/scala/akka/http/impl/server/Util.scala b/akka-http/src/main/scala/akka/http/impl/server/Util.scala index 96c4390756..fa03d0e2fd 100644 --- a/akka-http/src/main/scala/akka/http/impl/server/Util.scala +++ b/akka-http/src/main/scala/akka/http/impl/server/Util.scala @@ -7,6 +7,7 @@ package akka.http.impl.server import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromStringUnmarshaller } import akka.http.scaladsl.util.FastFuture import akka.japi.function +import akka.stream.Materializer import scala.concurrent.{ Future, ExecutionContext } import scala.util.Try @@ -17,7 +18,7 @@ object Util { def scalaUnmarshallerFromFunction[T, U](convert: function.Function[T, U]): Unmarshaller[T, U] = new Unmarshaller[T, U] { - def apply(value: T)(implicit ec: ExecutionContext): Future[U] = FastFuture(Try(convert(value))) + def apply(value: T)(implicit ec: ExecutionContext, mat: Materializer): Future[U] = FastFuture(Try(convert(value))) } implicit class JApiFunctionAndThen[T, U](f1: function.Function[T, U]) { diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala index 77e1286b16..62fab13016 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/RequestVals.scala @@ -26,8 +26,8 @@ object RequestVals { def entityAs[T](unmarshaller: Unmarshaller[T]): RequestVal[T] = new ExtractingStandaloneExtractionImpl[T]()(unmarshaller.classTag) { def extract(ctx: server.RequestContext): Future[T] = { - val u = unmarshaller.asInstanceOf[UnmarshallerImpl[T]].scalaUnmarshaller(ctx.executionContext, ctx.materializer) - u(ctx.request)(ctx.executionContext) + val u = unmarshaller.asInstanceOf[UnmarshallerImpl[T]].scalaUnmarshaller + u(ctx.request)(ctx.executionContext, ctx.materializer) } } diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala index 54aadf1fdb..bcc9ee4756 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala @@ -14,23 +14,18 @@ import akka.util.ByteString import scala.reflect.ClassTag object Unmarshallers { - def String: Unmarshaller[String] = withMat(implicit mat ⇒ implicitly) - def ByteString: Unmarshaller[ByteString] = withMat(implicit mat ⇒ implicitly) - def ByteArray: Unmarshaller[Array[Byte]] = withMat(implicit mat ⇒ implicitly) - def CharArray: Unmarshaller[Array[Char]] = withMat(implicit mat ⇒ implicitly) + def String: Unmarshaller[String] = implicitInstance + def ByteString: Unmarshaller[ByteString] = implicitInstance + def ByteArray: Unmarshaller[Array[Byte]] = implicitInstance + def CharArray: Unmarshaller[Array[Char]] = implicitInstance def fromMessage[T](convert: Function[HttpMessage, T], clazz: Class[T]): Unmarshaller[T] = - new UnmarshallerImpl[T]({ (ec, mat) ⇒ - Util.scalaUnmarshallerFromFunction(convert) - })(ClassTag(clazz)) + new UnmarshallerImpl[T](Util.scalaUnmarshallerFromFunction(convert))(ClassTag(clazz)) def fromEntity[T](convert: Function[HttpEntity, T], clazz: Class[T]): Unmarshaller[T] = - new UnmarshallerImpl[T]({ (ec, mat) ⇒ - ScalaUnmarshaller.messageUnmarshallerFromEntityUnmarshaller(Util.scalaUnmarshallerFromFunction[HttpEntity, T](convert)) - })(ClassTag(clazz)) + new UnmarshallerImpl[T]( + ScalaUnmarshaller.messageUnmarshallerFromEntityUnmarshaller(Util.scalaUnmarshallerFromFunction[HttpEntity, T](convert)))(ClassTag(clazz)) - private def withMat[T: ClassTag](f: Materializer ⇒ FromMessageUnmarshaller[T]): Unmarshaller[T] = - new UnmarshallerImpl[T]({ (ec, mat) ⇒ - f(mat) - }) + private def implicitInstance[T: ClassTag](implicit um: FromMessageUnmarshaller[T]): Unmarshaller[T] = + new UnmarshallerImpl[T](um) } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala b/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala index cd6744aaad..b0ca5f726d 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/common/StrictForm.scala @@ -48,13 +48,13 @@ object StrictForm { private[StrictForm] final case class FromPart(value: Multipart.FormData.BodyPart.Strict) extends Field implicit def unmarshaller[T](implicit um: FieldUnmarshaller[T]): FromStrictFormFieldUnmarshaller[T] = - Unmarshaller(implicit ec ⇒ { + Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ { case FromString(value) ⇒ um.unmarshalString(value) case FromPart(value) ⇒ um.unmarshalPart(value) }) def unmarshallerFromFSU[T](fsu: FromStringUnmarshaller[T]): FromStrictFormFieldUnmarshaller[T] = - Unmarshaller(implicit ec ⇒ { + Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ { case FromString(value) ⇒ fsu(value) case FromPart(value) ⇒ fsu(value.entity.data.decodeString(value.entity.contentType.charset.nioCharset.name)) }) @@ -62,63 +62,63 @@ object StrictForm { @implicitNotFound("In order to unmarshal a `StrictForm.Field` to type `${T}` you need to supply a " + "`FromStringUnmarshaller[${T}]` and/or a `FromEntityUnmarshaller[${T}]`") sealed trait FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext): Future[T] - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext): Future[T] + def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer): Future[T] + def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer): Future[T] } object FieldUnmarshaller extends LowPrioImplicits { implicit def fromBoth[T](implicit fsu: FromStringUnmarshaller[T], feu: FromEntityUnmarshaller[T]) = new FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext) = fsu(value) - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext) = feu(value.entity) + def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = fsu(value) + def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = feu(value.entity) } } sealed abstract class LowPrioImplicits { implicit def fromFSU[T](implicit fsu: FromStringUnmarshaller[T]) = new FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext) = fsu(value) - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext) = + def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = fsu(value) + def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = fsu(value.entity.data.decodeString(value.entity.contentType.charset.nioCharset.name)) } implicit def fromFEU[T](implicit feu: FromEntityUnmarshaller[T]) = new FieldUnmarshaller[T] { - def unmarshalString(value: String)(implicit ec: ExecutionContext) = feu(HttpEntity(value)) - def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext) = feu(value.entity) + def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = feu(HttpEntity(value)) + def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = feu(value.entity) } } } implicit def unmarshaller(implicit formDataUM: FromEntityUnmarshaller[FormData], - multipartUM: FromEntityUnmarshaller[Multipart.FormData], - fm: Materializer): FromEntityUnmarshaller[StrictForm] = - Unmarshaller { implicit ec ⇒ - entity ⇒ + multipartUM: FromEntityUnmarshaller[Multipart.FormData]): FromEntityUnmarshaller[StrictForm] = + Unmarshaller.withMaterializer { implicit ec ⇒ + implicit fm ⇒ + entity ⇒ - def tryUnmarshalToQueryForm: Future[StrictForm] = - for (formData ← formDataUM(entity).fast) yield { - new StrictForm { - val fields = formData.fields.map { case (name, value) ⇒ name -> Field.FromString(value) }(collection.breakOut) + def tryUnmarshalToQueryForm: Future[StrictForm] = + for (formData ← formDataUM(entity).fast) yield { + new StrictForm { + val fields = formData.fields.map { case (name, value) ⇒ name -> Field.FromString(value) }(collection.breakOut) + } } + + def tryUnmarshalToMultipartForm: Future[StrictForm] = + for { + multiPartFD ← multipartUM(entity).fast + strictMultiPartFD ← multiPartFD.toStrict(10.seconds).fast // TODO: make timeout configurable + } yield { + new StrictForm { + val fields = strictMultiPartFD.strictParts.map { + case x: Multipart.FormData.BodyPart.Strict ⇒ x.name -> Field.FromPart(x) + }(collection.breakOut) + } + } + + tryUnmarshalToQueryForm.fast.recoverWith { + case Unmarshaller.UnsupportedContentTypeException(supported1) ⇒ + tryUnmarshalToMultipartForm.fast.recoverWith { + case Unmarshaller.UnsupportedContentTypeException(supported2) ⇒ + FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(supported1 ++ supported2)) + } } - - def tryUnmarshalToMultipartForm: Future[StrictForm] = - for { - multiPartFD ← multipartUM(entity).fast - strictMultiPartFD ← multiPartFD.toStrict(10.seconds).fast // TODO: make timeout configurable - } yield { - new StrictForm { - val fields = strictMultiPartFD.strictParts.map { - case x: Multipart.FormData.BodyPart.Strict ⇒ x.name -> Field.FromPart(x) - }(collection.breakOut) - } - } - - tryUnmarshalToQueryForm.fast.recoverWith { - case Unmarshaller.UnsupportedContentTypeException(supported1) ⇒ - tryUnmarshalToMultipartForm.fast.recoverWith { - case Unmarshaller.UnsupportedContentTypeException(supported2) ⇒ - FastFuture.failed(Unmarshaller.UnsupportedContentTypeException(supported1 ++ supported2)) - } - } } /** diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala index 174e39ecf2..fb03b647c3 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/FormFieldDirectives.scala @@ -73,6 +73,7 @@ object FormFieldDirectives extends FormFieldDirectives { private def fieldOfForm[T](fieldName: String, fu: Unmarshaller[Option[StrictForm.Field], T])(implicit sfu: SFU): RequestContext ⇒ Future[T] = { ctx ⇒ import ctx.executionContext + import ctx.materializer sfu(ctx.request.entity).fast.flatMap(form ⇒ fu(form field fieldName)) } private def filter[T](fieldName: String, fu: FSFFOU[T])(implicit sfu: SFU): Directive1[T] = @@ -114,6 +115,7 @@ object FormFieldDirectives extends FormFieldDirectives { private def repeatedFilter[T](fieldName: String, fu: FSFFU[T])(implicit sfu: SFU): Directive1[Iterable[T]] = extract { ctx ⇒ import ctx.executionContext + import ctx.materializer sfu(ctx.request.entity).fast.flatMap(form ⇒ Future.sequence(form.fields.collect { case (`fieldName`, value) ⇒ fu(value) })) }.flatMap { result ⇒ handleFieldResult(fieldName, result) diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala index e24351fa23..d5c6678fcf 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/MarshallingDirectives.scala @@ -24,6 +24,7 @@ trait MarshallingDirectives { def entity[T](um: FromRequestUnmarshaller[T]): Directive1[T] = extractRequestContext.flatMap[Tuple1[T]] { ctx ⇒ import ctx.executionContext + import ctx.materializer onComplete(um(ctx.request)) flatMap { case Success(value) ⇒ provide(value) case Failure(Unmarshaller.NoContentException) ⇒ reject(RequestEntityExpectedRejection) @@ -43,7 +44,9 @@ trait MarshallingDirectives { * You can use it do decouple marshaller resolution from request completion. */ def completeWith[T](marshaller: ToResponseMarshaller[T])(inner: (T ⇒ Unit) ⇒ Unit): Route = - extractExecutionContext { implicit ec ⇒ + extractRequestContext { ctx ⇒ + import ctx.executionContext + import ctx.materializer implicit val m = marshaller complete { val promise = Promise[T]() diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala index 1a7a63ce94..c4a13b0197 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/ParameterDirectives.scala @@ -98,6 +98,7 @@ object ParameterDirectives extends ParameterDirectives { private def filter[T](paramName: String, fsou: FSOU[T]): Directive1[T] = extractRequestContext flatMap { ctx ⇒ import ctx.executionContext + import ctx.materializer handleParamResult(paramName, fsou(ctx.request.uri.query get paramName)) } implicit def forString(implicit fsu: FSU[String]): ParamDefAux[String, Directive1[String]] = @@ -122,6 +123,7 @@ object ParameterDirectives extends ParameterDirectives { private def requiredFilter[T](paramName: String, fsou: FSOU[T], requiredValue: Any): Directive0 = extractRequestContext flatMap { ctx ⇒ import ctx.executionContext + import ctx.materializer onComplete(fsou(ctx.request.uri.query get paramName)) flatMap { case Success(value) if value == requiredValue ⇒ pass case _ ⇒ reject @@ -137,6 +139,7 @@ object ParameterDirectives extends ParameterDirectives { private def repeatedFilter[T](paramName: String, fsu: FSU[T]): Directive1[Iterable[T]] = extractRequestContext flatMap { ctx ⇒ import ctx.executionContext + import ctx.materializer handleParamResult(paramName, Future.sequence(ctx.request.uri.query.getAll(paramName).map(fsu.apply))) } implicit def forRepVR[T](implicit fsu: FSU[T]): ParamDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] = diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala index c129410640..992a314daf 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala @@ -17,12 +17,12 @@ trait GenericUnmarshallers extends LowerPriorityGenericUnmarshallers { sealed trait LowerPriorityGenericUnmarshallers { implicit def messageUnmarshallerFromEntityUnmarshaller[T](implicit um: FromEntityUnmarshaller[T]): FromMessageUnmarshaller[T] = - Unmarshaller { implicit ec ⇒ request ⇒ um(request.entity) } + Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat ⇒ request ⇒ um(request.entity) } implicit def liftToSourceOptionUnmarshaller[A, B](um: Unmarshaller[A, B]): Unmarshaller[Option[A], B] = sourceOptionUnmarshaller(um) implicit def sourceOptionUnmarshaller[A, B](implicit um: Unmarshaller[A, B]): Unmarshaller[Option[A], B] = - Unmarshaller(implicit ec ⇒ { + Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ { case Some(a) ⇒ um(a) case None ⇒ FastFuture.failed(Unmarshaller.NoContentException) }) diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala index 6d2a65cc19..dba637eb7e 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala @@ -4,39 +4,38 @@ package akka.http.scaladsl.unmarshalling -import akka.stream.Materializer import akka.util.ByteString import akka.http.scaladsl.util.FastFuture import akka.http.scaladsl.model._ trait PredefinedFromEntityUnmarshallers extends MultipartUnmarshallers { - implicit def byteStringUnmarshaller(implicit fm: Materializer): FromEntityUnmarshaller[ByteString] = - Unmarshaller(_ ⇒ { + implicit def byteStringUnmarshaller: FromEntityUnmarshaller[ByteString] = + Unmarshaller.withMaterializer(_ ⇒ implicit mat ⇒ { case HttpEntity.Strict(_, data) ⇒ FastFuture.successful(data) case entity ⇒ entity.dataBytes.runFold(ByteString.empty)(_ ++ _) }) - implicit def byteArrayUnmarshaller(implicit fm: Materializer): FromEntityUnmarshaller[Array[Byte]] = + implicit def byteArrayUnmarshaller: FromEntityUnmarshaller[Array[Byte]] = byteStringUnmarshaller.map(_.toArray[Byte]) - implicit def charArrayUnmarshaller(implicit fm: Materializer): FromEntityUnmarshaller[Array[Char]] = - byteStringUnmarshaller(fm) mapWithInput { (entity, bytes) ⇒ + implicit def charArrayUnmarshaller: FromEntityUnmarshaller[Array[Char]] = + byteStringUnmarshaller mapWithInput { (entity, bytes) ⇒ val charBuffer = entity.contentType.charset.nioCharset.decode(bytes.asByteBuffer) val array = new Array[Char](charBuffer.length()) charBuffer.get(array) array } - implicit def stringUnmarshaller(implicit fm: Materializer): FromEntityUnmarshaller[String] = - byteStringUnmarshaller(fm) mapWithInput { (entity, bytes) ⇒ + implicit def stringUnmarshaller: FromEntityUnmarshaller[String] = + byteStringUnmarshaller mapWithInput { (entity, bytes) ⇒ // FIXME: add `ByteString::decodeString(java.nio.Charset): String` overload!!! bytes.decodeString(entity.contentType.charset.nioCharset.name) // ouch!!! } - implicit def defaultUrlEncodedFormDataUnmarshaller(implicit fm: Materializer): FromEntityUnmarshaller[FormData] = + implicit def defaultUrlEncodedFormDataUnmarshaller: FromEntityUnmarshaller[FormData] = urlEncodedFormDataUnmarshaller(MediaTypes.`application/x-www-form-urlencoded`) - def urlEncodedFormDataUnmarshaller(ranges: ContentTypeRange*)(implicit fm: Materializer): FromEntityUnmarshaller[FormData] = + def urlEncodedFormDataUnmarshaller(ranges: ContentTypeRange*): FromEntityUnmarshaller[FormData] = stringUnmarshaller.forContentTypes(ranges: _*).mapWithInput { (entity, string) ⇒ try { val nioCharset = entity.contentType.definedCharset.getOrElse(HttpCharsets.`UTF-8`).nioCharset diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala index c15fa2acd2..254c85ad5f 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala @@ -6,64 +6,75 @@ package akka.http.scaladsl.unmarshalling trait PredefinedFromStringUnmarshallers { - implicit val byteFromStringUnmarshaller = Unmarshaller.strict[String, Byte] { string ⇒ - try string.toByte - catch numberFormatError(string, "8-bit signed integer") - } - - implicit val shortFromStringUnmarshaller = Unmarshaller.strict[String, Short] { string ⇒ - try string.toShort - catch numberFormatError(string, "16-bit signed integer") - } - - implicit val intFromStringUnmarshaller = Unmarshaller.strict[String, Int] { string ⇒ - try string.toInt - catch numberFormatError(string, "32-bit signed integer") - } - - implicit val longFromStringUnmarshaller = Unmarshaller.strict[String, Long] { string ⇒ - try string.toLong - catch numberFormatError(string, "64-bit signed integer") - } - - val HexByte = Unmarshaller.strict[String, Byte] { string ⇒ - try java.lang.Byte.parseByte(string, 16) - catch numberFormatError(string, "8-bit hexadecimal integer") - } - - val HexShort = Unmarshaller.strict[String, Short] { string ⇒ - try java.lang.Short.parseShort(string, 16) - catch numberFormatError(string, "16-bit hexadecimal integer") - } - - val HexInt = Unmarshaller.strict[String, Int] { string ⇒ - try java.lang.Integer.parseInt(string, 16) - catch numberFormatError(string, "32-bit hexadecimal integer") - } - - val HexLong = Unmarshaller.strict[String, Long] { string ⇒ - try java.lang.Long.parseLong(string, 16) - catch numberFormatError(string, "64-bit hexadecimal integer") - } - - implicit val floatFromStringUnmarshaller = Unmarshaller.strict[String, Float] { string ⇒ - try string.toFloat - catch numberFormatError(string, "32-bit floating point") - } - - implicit val doubleFromStringUnmarshaller = Unmarshaller.strict[String, Double] { string ⇒ - try string.toDouble - catch numberFormatError(string, "64-bit floating point") - } - - implicit val booleanFromStringUnmarshaller = Unmarshaller.strict[String, Boolean] { string ⇒ - string.toLowerCase match { - case "true" | "yes" | "on" ⇒ true - case "false" | "no" | "off" ⇒ false - case "" ⇒ throw Unmarshaller.NoContentException - case x ⇒ throw new IllegalArgumentException(s"'$x' is not a valid Boolean value") + implicit val byteFromStringUnmarshaller: Unmarshaller[String, Byte] = + Unmarshaller.strict[String, Byte] { string ⇒ + try string.toByte + catch numberFormatError(string, "8-bit signed integer") + } + + implicit val shortFromStringUnmarshaller: Unmarshaller[String, Short] = + Unmarshaller.strict[String, Short] { string ⇒ + try string.toShort + catch numberFormatError(string, "16-bit signed integer") + } + + implicit val intFromStringUnmarshaller: Unmarshaller[String, Int] = + Unmarshaller.strict[String, Int] { string ⇒ + try string.toInt + catch numberFormatError(string, "32-bit signed integer") + } + + implicit val longFromStringUnmarshaller: Unmarshaller[String, Long] = + Unmarshaller.strict[String, Long] { string ⇒ + try string.toLong + catch numberFormatError(string, "64-bit signed integer") + } + + val HexByte: Unmarshaller[String, Byte] = + Unmarshaller.strict[String, Byte] { string ⇒ + try java.lang.Byte.parseByte(string, 16) + catch numberFormatError(string, "8-bit hexadecimal integer") + } + + val HexShort: Unmarshaller[String, Short] = + Unmarshaller.strict[String, Short] { string ⇒ + try java.lang.Short.parseShort(string, 16) + catch numberFormatError(string, "16-bit hexadecimal integer") + } + + val HexInt: Unmarshaller[String, Int] = + Unmarshaller.strict[String, Int] { string ⇒ + try java.lang.Integer.parseInt(string, 16) + catch numberFormatError(string, "32-bit hexadecimal integer") + } + + val HexLong: Unmarshaller[String, Long] = + Unmarshaller.strict[String, Long] { string ⇒ + try java.lang.Long.parseLong(string, 16) + catch numberFormatError(string, "64-bit hexadecimal integer") + } + + implicit val floatFromStringUnmarshaller: Unmarshaller[String, Float] = + Unmarshaller.strict[String, Float] { string ⇒ + try string.toFloat + catch numberFormatError(string, "32-bit floating point") + } + + implicit val doubleFromStringUnmarshaller: Unmarshaller[String, Double] = + Unmarshaller.strict[String, Double] { string ⇒ + try string.toDouble + catch numberFormatError(string, "64-bit floating point") + } + + implicit val booleanFromStringUnmarshaller: Unmarshaller[String, Boolean] = + Unmarshaller.strict[String, Boolean] { string ⇒ + string.toLowerCase match { + case "true" | "yes" | "on" ⇒ true + case "false" | "no" | "off" ⇒ false + case "" ⇒ throw Unmarshaller.NoContentException + case x ⇒ throw new IllegalArgumentException(s"'$x' is not a valid Boolean value") + } } - } private def numberFormatError(value: String, target: String): PartialFunction[Throwable, Nothing] = { case e: NumberFormatException ⇒ diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala index c903f7914a..c577221926 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshal.scala @@ -4,6 +4,8 @@ package akka.http.scaladsl.unmarshalling +import akka.stream.Materializer + import scala.concurrent.{ ExecutionContext, Future } object Unmarshal { @@ -14,5 +16,5 @@ class Unmarshal[A](val value: A) { /** * Unmarshals the value to the given Type using the in-scope Unmarshaller. */ - def to[B](implicit um: Unmarshaller[A, B], ec: ExecutionContext): Future[B] = um(value) + def to[B](implicit um: Unmarshaller[A, B], ec: ExecutionContext, mat: Materializer): Future[B] = um(value) } diff --git a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala index 1ff02b700e..b0383f027b 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala @@ -4,6 +4,8 @@ package akka.http.scaladsl.unmarshalling +import akka.stream.Materializer + import scala.util.control.{ NoStackTrace, NonFatal } import scala.concurrent.{ Future, ExecutionContext } import akka.http.scaladsl.util.FastFuture @@ -12,22 +14,22 @@ import akka.http.scaladsl.model._ trait Unmarshaller[-A, B] { - def apply(value: A)(implicit ec: ExecutionContext): Future[B] + def apply(value: A)(implicit ec: ExecutionContext, materializer: Materializer): Future[B] - def transform[C](f: ExecutionContext ⇒ Future[B] ⇒ Future[C]): Unmarshaller[A, C] = - Unmarshaller { implicit ec ⇒ a ⇒ f(ec)(this(a)) } + def transform[C](f: ExecutionContext ⇒ Materializer ⇒ Future[B] ⇒ Future[C]): Unmarshaller[A, C] = + Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat ⇒ a ⇒ f(ec)(mat)(this(a)) } def map[C](f: B ⇒ C): Unmarshaller[A, C] = - transform(implicit ec ⇒ _.fast map f) + transform(implicit ec ⇒ _ ⇒ _.fast map f) - def flatMap[C](f: ExecutionContext ⇒ B ⇒ Future[C]): Unmarshaller[A, C] = - transform(implicit ec ⇒ _.fast flatMap f(ec)) + def flatMap[C](f: ExecutionContext ⇒ Materializer ⇒ B ⇒ Future[C]): Unmarshaller[A, C] = + transform(implicit ec ⇒ mat ⇒ _.fast flatMap f(ec)(mat)) - def recover[C >: B](pf: ExecutionContext ⇒ PartialFunction[Throwable, C]): Unmarshaller[A, C] = - transform(implicit ec ⇒ _.fast recover pf(ec)) + def recover[C >: B](pf: ExecutionContext ⇒ Materializer ⇒ PartialFunction[Throwable, C]): Unmarshaller[A, C] = + transform(implicit ec ⇒ mat ⇒ _.fast recover pf(ec)(mat)) def withDefaultValue[BB >: B](defaultValue: BB): Unmarshaller[A, BB] = - recover(_ ⇒ { case Unmarshaller.NoContentException ⇒ defaultValue }) + recover(_ ⇒ _ ⇒ { case Unmarshaller.NoContentException ⇒ defaultValue }) } object Unmarshaller @@ -41,17 +43,20 @@ object Unmarshaller /** * Creates an `Unmarshaller` from the given function. */ - def apply[A, B](f: ExecutionContext ⇒ A ⇒ Future[B]): Unmarshaller[A, B] = + def apply[A, B](f: ExecutionContext ⇒ A ⇒ Future[B]): Unmarshaller[A, B] = + withMaterializer(ec => _ => f(ec)) + + def withMaterializer[A, B](f: ExecutionContext ⇒ Materializer => A ⇒ Future[B]): Unmarshaller[A, B] = new Unmarshaller[A, B] { - def apply(a: A)(implicit ec: ExecutionContext) = - try f(ec)(a) + def apply(a: A)(implicit ec: ExecutionContext, materializer: Materializer) = + try f(ec)(materializer)(a) catch { case NonFatal(e) ⇒ FastFuture.failed(e) } } /** * Helper for creating a synchronous `Unmarshaller` from the given function. */ - def strict[A, B](f: A ⇒ B): Unmarshaller[A, B] = Unmarshaller(_ ⇒ a ⇒ FastFuture.successful(f(a))) + def strict[A, B](f: A ⇒ B): Unmarshaller[A, B] = Unmarshaller(_ => a ⇒ FastFuture.successful(f(a))) /** * Helper for creating a "super-unmarshaller" from a sequence of "sub-unmarshallers", which are tried @@ -60,7 +65,7 @@ object Unmarshaller */ def firstOf[A, B](unmarshallers: Unmarshaller[A, B]*): Unmarshaller[A, B] = //... //# - Unmarshaller { implicit ec ⇒ a ⇒ + Unmarshaller.withMaterializer { implicit ec ⇒ implicit mat => a ⇒ def rec(ix: Int, supported: Set[ContentTypeRange]): Future[B] = if (ix < unmarshallers.size) { unmarshallers(ix)(a).fast.recoverWith { @@ -77,10 +82,10 @@ object Unmarshaller // we don't define these methods directly on `Unmarshaller` due to variance constraints implicit class EnhancedUnmarshaller[A, B](val um: Unmarshaller[A, B]) extends AnyVal { def mapWithInput[C](f: (A, B) ⇒ C): Unmarshaller[A, C] = - Unmarshaller(implicit ec ⇒ a ⇒ um(a).fast.map(f(a, _))) + Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ a ⇒ um(a).fast.map(f(a, _))) def flatMapWithInput[C](f: (A, B) ⇒ Future[C]): Unmarshaller[A, C] = - Unmarshaller(implicit ec ⇒ a ⇒ um(a).fast.flatMap(f(a, _))) + Unmarshaller.withMaterializer(implicit ec ⇒ implicit mat ⇒ a ⇒ um(a).fast.flatMap(f(a, _))) } implicit class EnhancedFromEntityUnmarshaller[A](val underlying: FromEntityUnmarshaller[A]) extends AnyVal { @@ -93,18 +98,19 @@ object Unmarshaller * this filter is *replaced* by this method, not stacked! */ def forContentTypes(ranges: ContentTypeRange*): FromEntityUnmarshaller[A] = - Unmarshaller { implicit ec ⇒ - entity ⇒ - if (entity.contentType == ContentTypes.NoContentType || ranges.exists(_ matches entity.contentType)) { - underlying(entity).fast recoverWith retryWithPatchedContentType(underlying, entity) - } else FastFuture.failed(UnsupportedContentTypeException(ranges: _*)) + Unmarshaller.withMaterializer { implicit ec ⇒ + implicit mat ⇒ + entity ⇒ + if (entity.contentType == ContentTypes.NoContentType || ranges.exists(_ matches entity.contentType)) { + underlying(entity).fast recoverWith retryWithPatchedContentType(underlying, entity) + } else FastFuture.failed(UnsupportedContentTypeException(ranges: _*)) } } // must be moved out of the the [[EnhancedFromEntityUnmarshaller]] value class due to bug in scala 2.10: // https://issues.scala-lang.org/browse/SI-8018 private def retryWithPatchedContentType[T](underlying: FromEntityUnmarshaller[T], entity: HttpEntity)( - implicit ec: ExecutionContext): PartialFunction[Throwable, Future[T]] = { + implicit ec: ExecutionContext, mat: Materializer): PartialFunction[Throwable, Future[T]] = { case UnsupportedContentTypeException(supported) ⇒ underlying(entity withContentType supported.head.specimen) }