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

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

问题描述

我试图了解Play 2.0框架的响应式I/O概念.为了从一开始就更好地理解,我决定跳过框架的助手来构造不同种类的迭代器,并从头开始编写自定义的Iteratee,以供BodyParser用来解析请求正文.

Iteratees

(备注:所有这些对我来说都是新事物,因此,如果有关此事完全是废话,请原谅.) Iteratee非常笨,它只读取所有块,对接收到的字节数求和,并输出一些消息.当我用一些数据调用控制器操作时,一切都会按预期进行-我可以观察到Iteratee接收到所有块,并且在读取所有数据时,它会切换为完成状态,并且请求结束.

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

  • 在读取所有输入之前切换到状态错误.
  • 切换到读取所有输入之前完成的状态,并返回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
'Cont El([B@38ecece6) 8192
'Error El([B@4ab50d3c) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error

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

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

15    case 'Done => { println("Done with " + input); 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
'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)

然后请求将永远挂起.

我的主要问题是为什么在上述情况下请求被挂起.如果有人可以阐明这一点,我将不胜感激!

解决方案

您的理解是完全正确的,我只是将一个修复程序推向了精通:

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

修复了两种情况以及Iteratees中的异常.

在案例类中很好地使用副本来进行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.

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

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