为什么调用错误或在 BodyParser 的 Iteratee 中完成请求在 Play Framework 2.0 中挂起? [英] Why makes calling error or done in a BodyParser's Iteratee the request hang in Play Framework 2.0?

查看:19
本文介绍了为什么调用错误或在 BodyParser 的 Iteratee 中完成请求在 Play Framework 2.0 中挂起?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试理解 Play 2.0 框架的反应式 I/O 概念.为了从一开始就获得更好的理解,我决定跳过框架的帮助程序来构建不同类型的迭代器,并从头开始编写一个自定义 Iteratee 以供 BodyParser 使用> 解析请求正文.

IterateesScalaBodyParser 文档和两个关于响应式 I/O 的演示,这就是我想出的:

import play.api.mvc._导入 play.api.mvc.Results._导入 play.api.libs.iteratee.{Iteratee, Input}导入 play.api.libs.concurrent.Promise导入 play.api.libs.iteratee.Input.{El, EOF, Empty}01 对象上传扩展控制器{02 def send = Action(BodyParser(rh => new SomeIteratee)) { request =>03 好的(完成")04 }05 }0607 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Each[Result, Int]] {08 println(状态 + " " + 输入 + " " + 接收)0910 折[B](11 完成:(Either[Result, Int], Input[Array[Byte]]) =>承诺[B],12 续:(Input[Array[Byte]] => Iteratee[Array[Byte],Either[Result, Int]]) =>承诺[B],13 错误:(String, Input[Array[Byte]]) =>承诺[B]14 ): Promise[B] = 状态匹配 {15 case 'Done =>{ println("完成");done(Right(received), Input.Empty) }16 case 'Cont =>继续(在 => 匹配 {17 情况下:El[Array[Byte]] =>复制(输入=输入,接收=接收+输入长度)18 情况下空 =>复制(输入 = 输入)19例EOF =>复制(状态 = '完成,输入 = 中)20 案例 _ =>复制(状态 = '错误,输入 = 中)21 })22 案例 _ =>{ println("错误");错误(一些错误.",输入)}23 }24 }

(备注:所有这些东西对我来说都是新的,所以如果这完全是废话,请原谅.)Iteratee 非常愚蠢,它只是读取所有块,汇总接收到的字节数并打印出一些消息.当我用一些数据调用控制器操作时,一切都按预期工作 - 我可以观察到所有块都被 Iteratee 接收,当所有数据被读取时,它切换到完成状态并且请求结束.

现在我开始尝试代码,因为我想看看这两种情况下的行为:

  • 在读取所有输入之前切换到状态错误.
  • 在读取所有输入之前切换到 done 状态并返回 Result 而不是 Int.

我对上述文档的理解是,两者都应该是可能的,但实际上我无法理解观察到的行为.为了测试第一种情况,我将上述代码的第 17 行更改为:

17 case in: El[Array[Byte]] =>copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)

所以我只是添加了一个条件,如果接收到超过 10000 个字节,则切换到错误状态.我得到的输出是这样的:

'Cont Empty 0'继续 El([B@38ecece6) 8192'错误 El([B@4ab50d3c) 16384错误错误错误错误错误错误错误错误错误错误错误

然后请求永远挂起并且永不结束.我对上述文档的期望是,当我在 Iteratee 的 fold 内调用 error 函数时,应该停止处理.这里发生的事情是,在调用 error 之后,Iteratee 的 fold 方法被调用了几次 - 好吧,然后请求挂起.

当我在读取所有输入之前切换到完成状态时,行为非常相似.将第 15 行更改为:

15 case 'Done =>{ println("完成" + 输入);done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }

和第 17 行:

17 case in: El[Array[Byte]] =>copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length)

产生以下输出:

'Cont Empty 0'继续 El([B@16ce00a8) 8192'完成 El([B@2e8d214a) 16384完成 El([B@2e8d214a)完成 El([B@2e8d214a)完成 El([B@2e8d214a)完成 El([B@2e8d214a)

请求再次永远挂起.

我的主要问题是为什么在上述情况下请求挂起.如果有人能对此有所了解,我将不胜感激!

解决方案

你的理解是完全正确的,我刚刚推送了一个修复方案给大师:

https://github.com/playframework/Play20/commit/ef70e641d92515b3b3d8d98d88d38d39bcc/p>

修复了迭代器中的两种情况和异常.

在 case 类中很好地使用副本来进行 Iteratee BTW.

I am trying to understand the reactive I/O concepts of Play 2.0 framework. In order to get a better understanding from the start I decided to skip the framework's helpers to construct iteratees of different kinds and to write a custom Iteratee from scratch to be used by a BodyParser to parse a request body.

Starting with the information available in Iteratees and ScalaBodyParser docs and two presentations about play reactive I/O this is what I came up with:

import play.api.mvc._
import play.api.mvc.Results._
import play.api.libs.iteratee.{Iteratee, Input}
import play.api.libs.concurrent.Promise
import play.api.libs.iteratee.Input.{El, EOF, Empty}

01 object Upload extends Controller {
02   def send = Action(BodyParser(rh => new SomeIteratee)) { request =>
03     Ok("Done")
04   }
05 }
06
07 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Either[Result, Int]] {
08   println(state + " " + input + " " + received)
09
10   def fold[B](
11     done: (Either[Result, Int], Input[Array[Byte]]) => Promise[B],
12     cont: (Input[Array[Byte]] => Iteratee[Array[Byte], Either[Result, Int]]) => Promise[B],
13     error: (String, Input[Array[Byte]]) => Promise[B]
14   ): Promise[B] = state match {
15     case 'Done => { println("Done"); done(Right(received), Input.Empty) }
16     case 'Cont => cont(in => in match {
17       case in: El[Array[Byte]] => copy(input = in, received = received + in.e.length)
18       case Empty => copy(input = in)
19       case EOF => copy(state = 'Done, input = in)
20       case _ => copy(state = 'Error, input = in)
21     })
22     case _ => { println("Error"); error("Some error.", input) }
23   }
24 }

(Remark: All these things are new to me, so please forgive if something about this is total crap.) The Iteratee is pretty dumb, it just reads all chunks, sums up the number of received bytes and prints out some messages. Everything works as expected when I call the controller action with some data - I can observe all chunks are received by the Iteratee and when all data is read it switches to state done and the request ends.

Now I started to play around with the code because I wanted to see the behaviour for these two cases:

  • Switching into state error before all input is read.
  • Switching into state done before all input is read and returning a Result instead of the Int.

My understanding of the documentation mentioned above is that both should be possible but actually I am not able to understand the observed behaviour. To test the first case I changed line 17 of the above code to:

17       case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)

So I just added a condition to switch into the error state if more than 10000 bytes were received. The output I get is this:

'Cont Empty 0
'Cont El([B@38ecece6) 8192
'Error El([B@4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error

Then the request hangs forever and never ends. My expectation from the above mentioned docs was that when I call the error function inside fold of an Iteratee the processing should be stopped. What is happening here is that the Iteratee's fold method is called several times after error has been called - well and then the request hangs.

When I switch into done state before reading all input the behaviour is quite similar. Changing line 15 to:

15    case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }

and line 17 to:

17       case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length)

produces the following output:

'Cont Empty 0
'Cont El([B@16ce00a8) 8192
'Done El([B@2e8d214a) 16384
Done with El([B@2e8d214a)
Done with El([B@2e8d214a)
Done with El([B@2e8d214a)
Done with El([B@2e8d214a)

and again the request hangs forever.

My main question is why the request is hanging in the above mentioned cases. If anybody could shed light on this I would greatly appreciate it!

解决方案

Your understanding is perfectly right and I have just push a fix to master:

https://github.com/playframework/Play20/commit/ef70e641d9114ff8225332bf18b4dd995bd39bcc

Fixed both cases plus exceptions in the Iteratees.

Nice use of copy in case class for doing an Iteratee BTW.

这篇关于为什么调用错误或在 BodyParser 的 Iteratee 中完成请求在 Play Framework 2.0 中挂起?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆