为什么调用错误或在 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?
问题描述
我正在尝试理解 Play 2.0 框架的反应式 I/O 概念.为了从一开始就获得更好的理解,我决定跳过框架的帮助程序来构建不同类型的迭代器,并从头开始编写一个自定义 Iteratee
以供 BodyParser
使用> 解析请求正文.
从 Iteratees 和 ScalaBodyParser 文档和两个关于响应式 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 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: (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: 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: 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: Then the request hangs forever and never ends. My expectation from the above mentioned docs was that when I call the When I switch into done state before reading all input the behaviour is quite similar. Changing line 15 to: and line 17 to: produces the following output: 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屋!Iteratee
from scratch to be used by a BodyParser
to parse a request body. 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 }
Result
instead of the Int
.17 case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length)
'Cont Empty 0
'Cont El([B@38ecece6) 8192
'Error El([B@4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
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.15 case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) }
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
'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)