Распаковка содержимого GZIP для асинхронного клиента Apache Http

В классическом HttpClient распаковке GZIP по умолчанию занимается ContentCompressionExec. Как это делается с HttpAsyncClient? Я не могу найти ни одного AsyncExecChainHandler, реализующего эту функциональность.


person Martijn    schedule 01.04.2020    source источник


Ответы (1)


В итоге я реализовал следующее AsyncResponseConsumer в scala.

class SimpleDecompressingResponseConsumer(val entityConsumer: AsyncEntityConsumer[Array[Byte]])
  extends AbstractAsyncResponseConsumer[SimpleHttpResponse, Array[Byte]](entityConsumer) {
  override def informationResponse(response: HttpResponse, context: HttpContext): Unit = ()

  override protected def buildResult(response: HttpResponse, entity: Array[Byte], contentType: ContentType): SimpleHttpResponse = {
    val simpleResponse = SimpleHttpResponse.copy(response)
    if (entity != null) simpleResponse.setBody(entity, contentType)
    simpleResponse
  }
}
class SimpleAsyncDecompressingEntityConsumer extends AbstractBinDataConsumer with AsyncEntityConsumer[Array[Byte]] {
  @volatile
  private var resultCallback: FutureCallback[Array[Byte]] = _
  private var encoding: Array[Byte] => Array[Byte] = _
  private var content: Array[Byte] = _

  private val buffer = new ByteArrayBuffer(1024)

  override def streamStart(entityDetails: EntityDetails, resultCallback: FutureCallback[Array[Byte]]): Unit = {
    this.resultCallback = resultCallback
    this.encoding = entityDetails.getContentEncoding match {
      case "gzip" | "x-gzip" =>
        bytes => IOUtils.toByteArray(new GZIPInputStream(new ByteArrayInputStream(bytes)))
      case "deflate" =>
        bytes => IOUtils.toByteArray(new DeflateInputStream(new ByteArrayInputStream(bytes)))
      case _ =>
        identity
    }
  }

  override def failed(cause: Exception): Unit = {
    if (resultCallback != null) resultCallback.failed(cause)
    releaseResources()
  }

  override def getContent: Array[Byte] = content

  override def capacityIncrement(): Int = Int.MaxValue

  override def data(src: ByteBuffer, endOfStream: Boolean): Unit = {
    if (src == null) return
    if (src.hasArray) buffer.append(src.array(), src.arrayOffset() + src.position(), src.remaining())
    else while (src.hasRemaining) buffer.append(src.get)
  }

  override def completed(): Unit = {
    this.content = encoding(buffer.toByteArray)
    if (resultCallback != null) resultCallback.completed(content)
    releaseResources()
  }

  override def releaseResources(): Unit = buffer.clear()
}
person Martijn    schedule 01.04.2020
comment
Выглядит разумно для небольших сообщений. - person ok2c; 02.04.2020
comment
@ ok2c Да, действительно отлично работает. Я использую клиент Async, потому что мне нужна поддержка HTTP2, которой нет в клиенте Classic. - person Martijn; 02.04.2020