Почему происходит ошибка вызова или в Iteratee BodyParser запрос зависает в Play Framework 2.0?

Я пытаюсь понять концепции реактивного ввода-вывода в Play 2.0 Framework. Чтобы лучше понять с самого начала, я решил пропустить помощники фреймворка, чтобы создавать итерации разных типов и писать собственныеIteratee с нуля для использованияBodyParser разобрать тело запроса.

Начиная с информации, доступной вIteratees а такжеScalaBodyParser документы и две презентации об игровом реактивном вводе / выводе, вот что я придумал:

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 }

(Примечание:All эти вещи являются новыми для меня, поэтому, пожалуйста, прости, если что-то об этом полная чушь.) Итератор довольно тупой, он просто читает все порции, суммирует количество полученных байтов и распечатывает некоторые сообщения. Все работает, как и ожидалось, когда я вызываю действие контроллера с некоторыми данными - я вижу, что Iteratee получает все куски, и когда все данные читаются, он переключается в состояние «выполнено», и запрос заканчивается.

Теперь я начал играть с кодом, потому что хотел увидеть поведение для этих двух случаев:

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.

Мое понимание документации, упомянутой выше, состоит в том, что оба должны быть возможны, но на самом деле я не могу понять наблюдаемое поведение. Для проверки первого случая я изменил строку 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([[email protected]) 8192
'Error El([[email protected]) 16384
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error
Error

Тогда запрос зависает навсегда и никогда не заканчивается. Я ожидал от вышеупомянутых документов, что когда я звонюerror функция внутриfold Итератора обработка должна быть остановлена. То, что здесь происходит, заключается в том, что метод сгибов Iteratee вызывается несколько раз после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([[email protected]) 8192
'Done El([[email protected]) 16384
Done with El([[email protected])
Done with El([[email protected])
Done with El([[email protected])
Done with El([[email protected])

и снова запрос висит навсегда.

Мой главный вопрос, почему запрос висит в вышеупомянутых случаях. Если бы кто-нибудь мог пролить свет на это, я был бы очень признателен!

Ответы на вопрос(2)

аметрическим, и этот пример больше не компилируется.

Решение Вопроса

Ваше понимание совершенно верно, и я только что решил исправить это:

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

Исправлены оба случая плюс исключения в Итераторах.

Хорошее использование copy в случае case для выполнения Iteratee BTW.

 lost15 июл. 2012 г., 00:31
Спасибо за исправление и добрые слова, постараюсь исправить это как можно скорее!

Ваш ответ на вопрос