简体   繁体   English

Play2 Framework代理到客户端的流内容在流完成后保持连接打开

[英]Play2 Framework proxy streaming content to client keeps connection open after streaming is done

The below code does streaming back to client, in, what I gather is a more idiomatic way than using Java's IO Streams. 下面的代码确实流回了客户端,与使用Java的IO流相比,我收集的是一种更加惯用的方法。 It, however, has an issue: connection is kept open after stream is done. 但是,它有一个问题:流完成后,连接保持打开状态。

def getImage() = Action { request =>
  val imageUrl = "http://hereandthere.com/someimageurl.png"
  Ok.stream({ content: Iteratee[Array[Byte], Unit] => 
    WS.url(imageUrl).withHeaders("Accept"->"image/png").get { response => content }
    return
  }).withHeaders("Content-Type"->"image/png")
}

this is intended for streaming large (>1 mb) files from internal API to requester. 这用于将大型(> 1 mb)文件从内部API流式传输到请求者。

The question is, why does it keep the connection open? 问题是,为什么它使连接保持打开状态? Is there something it expects from upstream server? 上游服务器有什么期望吗? I tested the upstream server using curl, and the connection does close - it just doesn't close when passed through this proxy. 我使用curl测试了上游服务器,并且连接确实关闭-通过此代理传递时,连接没有关闭。

The reason that the stream doesn't finish is because an EOF isn't sent to the iteratee that comes back from WS.get() call. 流未完成的原因是因为未将EOF发送到从WS.get()调用返回的iteratee。 Without this explicit EOF, the connection stays open - as it's in chunked mode, and potentially a long-running, comet-like connection. 没有这种显式的EOF,连接将保持打开状态-因为它处于分块模式,并且可能会长期运行,就像彗星一样。

Here's the fixed code: 这是固定代码:

Ok.stream({ content: Iteratee[Array[Byte], Unit] => 
  WS.url(imageUrl)
    .withHeaders("Accept"->"image/png")
    .get { response => content }
    .onRedeem { ii =>
       ii.feed(Input.EOF)
    }
}).withHeaders("Content-Type"->"image/png")

Here is a modified version for play 2.1.0. 这是播放2.1.0的修改版本。 See https://groups.google.com/forum/#!msg/play-framework/HwoRR-nipCc/gUKs9NexCx4J 参见https://groups.google.com/forum/#!msg/play-framework/HwoRR-nipCc/gUKs9NexCx4J

Thanks Anatoly G for sharing. 感谢Anatoly G的分享。

def proxy = Action {

   val url = "..."

   Async {
     val iterateePromise = Promise[Iteratee[Array[Byte], Unit]]
     val resultPromise = Promise[ChunkedResult[Array[Byte]]]

     WS.url(url).get { responseHeaders =>
       resultPromise.success {
         new Status(responseHeaders.status).stream({ content: Iteratee[Array[Byte], Unit] =>
           iterateePromise.success(content)
         }).withHeaders(
           "Content-Type" -> responseHeaders.headers.getOrElse("Content-Type", Seq("application/octet-stream")).head,
           "Connection" -> "Close")
       }
       Iteratee.flatten(iterateePromise.future)
     }.onComplete {
       case Success(ii) => ii.feed(Input.EOF)
       case Failure(t) => resultPromise.failure(t)
     }

     resultPromise.future
   }

}

Update for play 2.2.x: 播放2.2.x的更新:

def proxy = Action.async {
  val url = "http://localhost:9000"

  def enumerator(chunks: Iteratee[Array[Byte], Unit] => _) = {
    new Enumerator[Array[Byte]] {
      def apply[C](i: Iteratee[Array[Byte], C]): Future[Iteratee[Array[Byte], C]] = {
        val doneIteratee = Promise[Iteratee[Array[Byte], C]]()
        chunks(i.map {
          done =>
            doneIteratee.success(Done[Array[Byte], C](done)).asInstanceOf[Unit]
        })
        doneIteratee.future
      }
    }
  }

  val iterateePromise = Promise[Iteratee[Array[Byte], Unit]]()
  val resultPromise = Promise[SimpleResult]()

  WS.url(url).get {
    responseHeaders =>

      resultPromise.success(new Status(responseHeaders.status).chunked(
        enumerator({
          content: Iteratee[Array[Byte], Unit] => iterateePromise.success(content)
        }
        )).withHeaders(
        "Content-Type" -> responseHeaders.headers.getOrElse("Content-Type", Seq("application/octet-stream")).head,
        "Connection" -> "Close"))

      Iteratee.flatten(iterateePromise.future)
  }.onComplete {
    case Success(ii) => ii.feed(Input.EOF)
    case Failure(t) => throw t
  }

  resultPromise.future
}

if anyone has a better solution, it interests me greatly! 如果有人有更好的解决方案,我会非常感兴趣!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM