5

Play 2.0 フレームワークのリアクティブ I/O の概念を理解しようとしています。最初から理解を深めるために、フレームワークのヘルパーをスキップしてさまざまな種類の iteratee を構築し、リクエスト本文を解析するためにIterateea によって使用されるカスタムをゼロから作成することにしました。BodyParser

IterateesScalaBodyParser のドキュメントで入手可能な情報と、リアクティブ I/O の再生に関する 2 つのプレゼンテーションから始めて、次のように思いつきました。

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 }

(注:これらはすべて私にとって初めてのことなので、これについての何かが完全にがらくたである場合はご容赦ください。) Iteratee はかなりばかげており、すべてのチャンクを読み取り、受信したバイト数を合計し、いくつかのメッセージを出力するだけです。一部のデータを使用してコントローラー アクションを呼び出すと、すべてが期待どおりに機能します。すべてのチャンクが Iteratee によって受信され、すべてのデータが読み取られると完了状態に切り替わり、要求が終了することを確認できます。

次の 2 つのケースの動作を確認したかったので、コードをいじり始めました。

  • すべての入力が読み取られる前にエラー状態に切り替わります。
  • すべての入力が読み込まれる前に完了状態に切り替え、 の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

その後、リクエストは永遠にハングアップし、決して終了しません。上記のドキュメントからの私の期待は、Iterateeerror内で関数を呼び出すとfold、処理を停止する必要があるということでした。ここで起こっていることは、Iteratee の fold メソッドが呼び出された後errorに数回呼び出されるということです。そうすると、リクエストがハングします。

すべての入力を読み取る前に完了状態に切り替えると、動作は非常に似ています。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)

また、リクエストは永遠にハングします。

私の主な質問は、上記のケースでリクエストがハングしている理由です。誰かがこれに光を当てることができれば、私はそれを大いに感謝します!

4

2 に答える 2

4

あなたの理解は完全に正しいので、マスターに修正をプッシュしました:

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

Iteratees の両方のケースと例外を修正しました。

ところで、Iteratee を実行するためのケース クラスでのコピーの適切な使用。

于 2012-07-14T22:15:39.507 に答える
0

Play 2.1 では状況が変わったに違いありません - Promise はパラメトリックではなくなり、この例はコンパイルされなくなりました。

于 2013-02-22T05:37:48.603 に答える