как работает bodyParser.Raw в игре! фреймворк работает?

#playframework #akka-stream #akka-http

#playframework #akka-stream #akka-http

Вопрос:

У меня вопрос о BodyParser.Raw в игре! фреймворк. В официальном документе говорится:

Анализирует тело как RawBuffer . При этом будет предпринята попытка сохранить тело в памяти, вплоть до размера буфера памяти, настроенного Play, но в случае превышения этого размера он будет записан в файл.

Мне трудно понять приведенное выше описание. Означает ли это следующее —

Если я добавлю в свой application.conf файл следующие конфигурации:

 play.http.parser.maxMemoryBuffer=128k
play.http.parser.maxDiskBuffer=1G
 

и скажите, что тело моего запроса имеет размер 15 МБ, затем играйте! будет считывать / анализировать первые 128 КБ из тела запроса, записывать его в файл, а затем передавать / анализировать следующие 128 КБ из тела запроса, записывать его в тот же файл, …, пока тело запроса размером 15 МБ не будет полностью проанализировано? Это означает, что мы используем только 128 КБ памяти и можем обрабатывать тело запроса размером 15 МБ??

Я знаю, это звучит слишком хорошо, чтобы быть правдой…

Не мог бы кто-нибудь любезно объяснить это?

Обновить:

Я хочу добавить некоторые обновления здесь, если кто-то столкнулся с такой же проблемой здесь. Итак, @Ivan Kurchenko прав (см. Ниже Его Ответ): BodyParser.Raw может использоваться для POST тела запроса, которое больше настроенного play.http.parser.maxMemoryBuffer . Играйте! в этом случае запрос будет буферизован с использованием диска. Другими словами, вы можете POST создать большое тело запроса, которое даже больше, чем доступная память в вашей JVM.

Причина, по которой мой тест не прошел с использованием Chrome (а также Firefox), заключалась в том, что у меня был включен CSRF-фильтр по умолчанию. Как только я отключил его, все работает нормально. Например, следующие конфигурации,

 play.http.parser.maxMemoryBuffer=1k
play.http.parser.maxDiskBuffer=4G
play.filters.disabled =play.filters.csrf.CSRFFilter
 

говорит, что если тело запроса больше 1 кб, играйте! будет использовать ваш диск для буферизации вашего запроса (и, конечно же, я отключаю фильтр CSRF). Теперь, с этой конфигурацией, я могу успешно загрузить фильм размером более 3G. Действительно, BodyParser.Raw может использоваться, если вам нужно обработать большой запрос. HTH!

Ответ №1:

Не совсем — если размер запроса превышает пороговое значение памяти, в вашем случае, настроенное как play.http.parser.maxMemoryBuffer=128k целое тело запроса, будет записано во временный файл или иным образом записано в память. И затем для следующего синтаксического анализа тело также будет передаваться либо из файла, либо из памяти. Давайте посмотрим вместе:

bodyParser.raw — создает RawBuffer внутри, как, например, здесь https://github.com/playframework/playframework/blob/6d0789468909d5d7bdabc6c4207337bd7a7ca9b1/core/play/src/main/scala/play/api/mvc/BodyParsers.scala#L612

И внутри RawBuffer двух методов, которые нас интересуют: реализация метода push, который принимает входящие байты, а общий размер превышает размер буфера, выполняется backToTemporaryFile , что означает запись всех запросов во временный файл.

   @volatile private var inMemory: ByteString                 = initialData
  @volatile private var backedByTemporaryFile: TemporaryFile = _
  @volatile private var outStream: OutputStream              = _

  private[play] def push(chunk: ByteString): Unit = {
    if (inMemory != null) { //checks whether current in memory buffer exists

      //if next readed chunk of request body exceeds in memory buffer size
      if (chunk.length   inMemory.size > memoryThreshold) {
        backToTemporaryFile()  // create temporary file
        outStream.write(chunk.toArray)// write buffer to temporary file
      } else {
        inMemory = inMemory    chunk // append in memory buffer with next chunk
      }
    } else {
      outStream.write(chunk.toArray) // append in memory buffer with next chunk
    }
  }
 

А затем реализация метода asBytes, которая на своей стороне считывает байты, если в буфере памяти существует. Или реализация asFile, которая считывает байты из временного файла.

Также обратите внимание: если размер тела входящего запроса превышает заданное значение, play.http.parser.maxDiskBuffer Play ответит статусом 413 — REQUEST_ENTITY_TOO_LARGE.

Комментарии:

1. Привет @Ivan, спасибо за объяснение! Методы чтения, на которые вы указали, действительно помогают. Еще несколько вещей: во-первых, настроенный порог памяти в моем случае play...maxMemoryBuffer=128k play...maxDiskBuffer=1G это размер буфера жесткого диска … так что, я думаю, это была опечатка в вашем ответе? Во-вторых, я написал крошечное приложение, чтобы протестировать это, для имитации большого тела запроса я загружал большой файл. Теперь, если `play …. maxMemoryBuffer’ настроен на < размер файла, Chrome сразу выдает «Этот сайт недоступен», похоже, логика, которую вы описали в своем ответе, никогда не срабатывала??

2. @lee Большое вам спасибо за внимание к деталям, да, вы правы, это была опечатка, а также я добавил фрагмент кода с некоторыми комментариями.

3. @lee Я не уверен насчет Chrome — для тестирования таких низкоуровневых вещей я предпочитаю использовать надлежащие инструменты, такие как Postman или curl, которые могут предоставить больше деталей низкого уровня HTTP.

4. @lee Я обновил ответ об ограничениях размера.

5. @lee я не уверен относительно rest Play! синтаксический анализатор — нужно посмотреть индивидуально. Но некоторые только в памяти — например, Json или String.