All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.http.DelayedReleaseService.scala Maven / Gradle / Ivy

package com.twitter.finagle.http

import com.twitter.finagle.client.StackClient
import com.twitter.finagle.util.AsyncLatch
import com.twitter.finagle._
import com.twitter.io.Reader
import com.twitter.util.{Future, Promise, Return, Throw, Time}
import java.util.concurrent.atomic.AtomicBoolean

private[finagle] object DelayedRelease {
  val role = StackClient.Role.prepFactory
  val description = "Prevents an HTTP service from being closed until its response completes"
  val module: Stackable[ServiceFactory[Request, Response]] =
    new Stack.Module1[FactoryToService.Enabled, ServiceFactory[Request, Response]] {
      val role = DelayedRelease.role
      val description = DelayedRelease.description
      def make(_enabled: FactoryToService.Enabled, next: ServiceFactory[Request, Response]) =
        if (_enabled.enabled) next.map(new DelayedReleaseService(_))
        else next
    }
}

/**
 * Delay release of the connection until all chunks have been received.
 */
private[finagle] class DelayedReleaseService[-Req <: Request](
  service: Service[Req, Response]
) extends ServiceProxy[Req, Response](service) {

  protected[this] val latch = new AsyncLatch

  private[this] def proxy(in: Response) = {
    val released = new AtomicBoolean(false)
    def done() {
      if (released.compareAndSet(false, true)) {
        latch.decr()
      }
    }

    Response(
      in.httpResponse,
      new Reader {
        def read(n: Int) = in.reader.read(n) respond {
          case Return(None) => done()
          case Throw(_) => done()
          case _ =>
        }

        def discard() = {
          // Note: Discarding the underlying reader terminates the session and
          // marks the service as unavailable. It's important that we discard
          // before releasing the service (by invoking `done`), to ensure that
          // the service wrapper in the pool will create a new service instead
          // of reusing this one whose transport is closing.
          in.reader.discard()
          done()
        }
      }
    )
  }

  override def apply(request: Req): Future[Response] = {
    latch.incr()
    service(request) transform {
      case Return(r) if r.isChunked =>
        Future.value(proxy(r))
      case t =>
        latch.decr()
        Future.const(t)
    }
  }

  override final def close(deadline: Time): Future[Unit] = {
    val p = new Promise[Unit]
    latch.await { p.become(service.close(deadline)) }
    p
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy