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

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

package com.twitter.finagle.http

import com.twitter.finagle.http.collection.RecordSchema
import com.twitter.finagle.http.util.FailingWriter
import com.twitter.io.{Buf, Pipe, Reader, Writer}

/**
 * Rich HttpResponse
 */
abstract class Response private extends Message {

  /**
   * Arbitrary user-defined context associated with this response object.
   * [[com.twitter.finagle.http.collection.RecordSchema.Record RecordSchema.Record]] is
   * used here, rather than [[com.twitter.finagle.context.Context]] or similar
   * out-of-band mechanisms, to make the connection between the response and its
   * associated context explicit.
   */
  def ctx: Response.Schema.Record

  final def isRequest = false

  def status: Status

  /**
   * Set the status of this response
   *
   * @note see [[status(Status)]] for Java users.
   */
  def status_=(value: Status): Unit

  /**
   * Set the status of this response
   *
   * @note See [[status_=(Status)]] for Scala users.
   */
  final def status(value: Status): this.type = {
    this.status = value
    this
  }

  /**
   * Get the status code of this response
   */
  final def statusCode: Int = status.code

  /**
   * Set the status code of this response
   *
   * @note See [[statusCode(Int)]] for Java users.
   */
  final def statusCode_=(value: Int): Unit = status = Status.fromCode(value)

  /**
   * Set the status code of this response
   *
   * @note See [[statusCode_=(Int)]] for Scala users.
   */
  final def statusCode(value: Int): this.type = {
    this.statusCode = value
    this
  }

  override def toString =
    "Response(\"" + version + " " + status + "\")"
}

object Response {

  /**
   * [[com.twitter.finagle.http.collection.RecordSchema RecordSchema]] declaration, used
   * to generate [[com.twitter.finagle.http.collection.RecordSchema.Record Record]] instances
   * for Response.ctx.
   */
  val Schema: RecordSchema = new RecordSchema

  /** Create Response. */
  def apply(): Response =
    apply(Version.Http11, Status.Ok)

  /** Create Response from status. */
  def apply(status: Status): Response =
    apply(Version.Http11, status)

  /** Create Response from version and status. */
  def apply(version: Version, status: Status): Response = {
    // Since this is a user made `Response` we use a Pipe so they
    // can keep a handle to the writer half and the server implementation can use
    // the reader half.
    val rw = new Pipe[Chunk]
    val resp = new Impl(rw, rw)
    resp.version = version
    resp.status = status
    resp
  }

  /**
   * Create a Response from version, status, and Reader.
   */
  def apply(version: Version, status: Status, reader: Reader[Buf]): Response = {
    chunked(version, status, reader)
  }

  private[finagle] def chunked(version: Version, status: Status, reader: Reader[Buf]): Response = {
    val resp = new Impl(reader.map(Chunk.apply))
    resp.version = version
    resp.status = status
    resp.setChunked(true)
    resp
  }

  /** Create 200 Response with the same HTTP version as the provided Request */
  def apply(request: Request): Response = apply(request.version, Status.Ok)

  /**
   * An inbound response (a response received by a client) that can include trailers.
   */
  private[finagle] final class Inbound(val chunkReader: Reader[Chunk], val trailers: HeaderMap)
      extends Response {

    @volatile private[this] var _status: Status = Status.Ok

    def chunkWriter: Writer[Chunk] = FailingWriter

    val headerMap: HeaderMap = HeaderMap()

    // Lazily created which allows those not using this functionality to not pay for it.
    @volatile private[this] var _ctx: Response.Schema.Record = _
    def ctx: Response.Schema.Record = {
      if (_ctx == null) synchronized {
        if (_ctx == null) _ctx = Response.Schema.newRecord()
      }
      _ctx
    }

    override def status: Status = _status
    override def status_=(value: Status): Unit = {
      _status = value
    }
  }

  /**
   * An outbound response (a response sent by a server).
   */
  private[finagle] final class Impl(val chunkReader: Reader[Chunk], val chunkWriter: Writer[Chunk])
      extends Response {

    def this(chunkReader: Reader[Chunk]) = this(chunkReader, FailingWriter)

    @volatile private[this] var _status: Status = Status.Ok

    val headerMap: HeaderMap = HeaderMap()

    // Lazily created which allows those not using this functionality to not pay for it.
    @volatile private[this] var _ctx: Response.Schema.Record = _
    def ctx: Response.Schema.Record = {
      if (_ctx == null) synchronized {
        if (_ctx == null) _ctx = Response.Schema.newRecord()
      }
      _ctx
    }

    def trailers: HeaderMap = HeaderMap.Empty

    override def status: Status = _status
    override def status_=(value: Status): Unit = {
      _status = value
    }
  }

  abstract class Proxy extends Response {

    /**
     * Underlying `Response`
     */
    def response: Response

    def chunkReader: Reader[Chunk] = response.chunkReader
    def chunkWriter: Writer[Chunk] = response.chunkWriter
    def ctx: Response.Schema.Record = response.ctx
    override lazy val cookies: CookieMap = response.cookies
    def headerMap: HeaderMap = response.headerMap
    def trailers: HeaderMap = response.trailers

    // These things should never need to be modified
    final def status: Status = response.status
    final def status_=(value: Status): Unit = response.status_=(value)
    final override def content: Buf = response.content
    final override def content_=(content: Buf): Unit = response.content_=(content)
    final override def version: Version = response.version
    final override def version_=(version: Version): Unit = response.version_=(version)
    final override def isChunked: Boolean = response.isChunked
    final override def setChunked(chunked: Boolean): Unit = response.setChunked(chunked)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy