=htc #16395 print decoded text for non-binary strict entities
* truncate strict entity before decoding on toString * print raw byte string if strict entity decoding fails
This commit is contained in:
parent
0f74a0e02b
commit
026e4f0078
2 changed files with 66 additions and 2 deletions
24
akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala
Normal file → Executable file
24
akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala
Normal file → Executable file
|
|
@ -16,10 +16,13 @@ import akka.stream.stage._
|
|||
import akka.stream._
|
||||
import akka.{ japi, stream }
|
||||
import akka.http.javadsl.model.HttpEntityStrict
|
||||
import akka.http.scaladsl.model.ContentType.{ NonBinary, Binary }
|
||||
import akka.http.scaladsl.util.FastFuture
|
||||
import akka.http.javadsl.{ model ⇒ jm }
|
||||
import akka.http.impl.util.JavaMapping.Implicits._
|
||||
|
||||
import scala.util.control.NonFatal
|
||||
|
||||
/**
|
||||
* Models the entity (aka "body" or "content) of an HTTP message.
|
||||
*/
|
||||
|
|
@ -239,6 +242,27 @@ object HttpEntity {
|
|||
else Default(contentType, data.length, limitableByteSource(Source.single(data))) withSizeLimit maxBytes
|
||||
|
||||
override def productPrefix = "HttpEntity.Strict"
|
||||
|
||||
override def toString = {
|
||||
val dataAsString = contentType match {
|
||||
case _: Binary ⇒
|
||||
data.toString()
|
||||
case nb: NonBinary ⇒
|
||||
try {
|
||||
val maxBytes = 4096
|
||||
if (data.length > maxBytes) {
|
||||
val truncatedString = data.take(maxBytes).decodeString(nb.charset.value).dropRight(1)
|
||||
s"$truncatedString ... (${data.length} bytes total)"
|
||||
} else
|
||||
data.decodeString(nb.charset.value)
|
||||
} catch {
|
||||
case NonFatal(e) ⇒
|
||||
data.toString()
|
||||
}
|
||||
}
|
||||
|
||||
s"$productPrefix($contentType,$dataAsString)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
44
akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala
Normal file → Executable file
44
akka-http-core/src/test/scala/akka/http/scaladsl/model/HttpEntitySpec.scala
Normal file → Executable file
|
|
@ -9,7 +9,7 @@ import com.typesafe.config.{ ConfigFactory, Config }
|
|||
import scala.concurrent.{ Promise, Await }
|
||||
import scala.concurrent.duration._
|
||||
import org.scalatest.{ BeforeAndAfterAll, MustMatchers, FreeSpec }
|
||||
import org.scalatest.matchers.Matcher
|
||||
import org.scalatest.matchers.{ MatchResult, Matcher }
|
||||
import akka.util.ByteString
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.scaladsl._
|
||||
|
|
@ -17,6 +17,8 @@ import akka.stream.ActorMaterializer
|
|||
import akka.http.scaladsl.model.HttpEntity._
|
||||
import akka.http.impl.util.StreamUtils
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
class HttpEntitySpec extends FreeSpec with MustMatchers with BeforeAndAfterAll {
|
||||
val tpe: ContentType = ContentTypes.`application/octet-stream`
|
||||
val abc = ByteString("abc")
|
||||
|
|
@ -28,7 +30,6 @@ class HttpEntitySpec extends FreeSpec with MustMatchers with BeforeAndAfterAll {
|
|||
akka.event-handlers = ["akka.testkit.TestEventListener"]
|
||||
akka.loglevel = WARNING""")
|
||||
implicit val system = ActorSystem(getClass.getSimpleName, testConf)
|
||||
import system.dispatcher
|
||||
|
||||
implicit val materializer = ActorMaterializer()
|
||||
override def afterAll() = system.shutdown()
|
||||
|
|
@ -117,6 +118,36 @@ class HttpEntitySpec extends FreeSpec with MustMatchers with BeforeAndAfterAll {
|
|||
transformTo(Strict(tpe, doubleChars("abcfghijk") ++ trailer))
|
||||
}
|
||||
}
|
||||
"support toString" - {
|
||||
"Strict with binary MediaType" in {
|
||||
val binaryType = ContentTypes.`application/octet-stream`
|
||||
val entity = Strict(binaryType, abc)
|
||||
entity must renderStrictDataAs(entity.data.toString())
|
||||
}
|
||||
"Strict with non-binary MediaType and less than 4096 bytes" in {
|
||||
val nonBinaryType = ContentTypes.`application/json`
|
||||
val entity = Strict(nonBinaryType, abc)
|
||||
entity must renderStrictDataAs(entity.data.decodeString(nonBinaryType.charset.value))
|
||||
}
|
||||
"Strict with non-binary MediaType and over 4096 bytes" in {
|
||||
val utf8Type = ContentTypes.`text/plain(UTF-8)`
|
||||
val longString = Random.alphanumeric.take(10000).mkString
|
||||
val entity = Strict(utf8Type, ByteString.apply(longString, utf8Type.charset.value))
|
||||
entity must renderStrictDataAs(s"${longString.take(4095)} ... (10000 bytes total)")
|
||||
}
|
||||
"Default" in {
|
||||
val entity = Default(tpe, 11, source(abc, de, fgh, ijk))
|
||||
entity.toString must include(entity.productPrefix)
|
||||
}
|
||||
"CloseDelimited" in {
|
||||
val entity = CloseDelimited(tpe, source(abc, de, fgh, ijk))
|
||||
entity.toString must include(entity.productPrefix)
|
||||
}
|
||||
"Chunked" in {
|
||||
val entity = Chunked(tpe, source(Chunk(abc)))
|
||||
entity.toString must include(entity.productPrefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def source[T](elems: T*) = Source(elems.toList)
|
||||
|
|
@ -136,6 +167,15 @@ class HttpEntitySpec extends FreeSpec with MustMatchers with BeforeAndAfterAll {
|
|||
Await.result(transformed.toStrict(250.millis), 250.millis)
|
||||
}
|
||||
|
||||
def renderStrictDataAs(dataRendering: String): Matcher[Strict] =
|
||||
Matcher { strict: Strict ⇒
|
||||
val expectedRendering = s"${strict.productPrefix}(${strict.contentType},$dataRendering)"
|
||||
MatchResult(
|
||||
strict.toString == expectedRendering,
|
||||
strict.toString + " != " + expectedRendering,
|
||||
strict.toString + " == " + expectedRendering)
|
||||
}
|
||||
|
||||
def duplicateBytesTransformer(): Flow[ByteString, ByteString, Unit] =
|
||||
Flow[ByteString].transform(() ⇒ StreamUtils.byteStringTransformer(doubleChars, () ⇒ trailer))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue