Приложение Cordova на Android с использованием отправки SSL multipart / form-data приводит к ошибке приложения Rack: # <EOFError: bad content body>

У нас есть приложение Cordova, которое подключается к серверу rails через SSL. Похоже, что наше недавнее изменение от Unicorn до Puma вызвало проблему, когда приложение Android получает 500 ошибок при отправке файла изображения на сервер.

Некоторые примечания:

  • Версия приложения iOS, построенная с использованием одного и того же кода, отлично работает
  • Все работает отлично, если не использовать SSL
  • Все остальные запросы – это SSL и работают нормально, только при отправке многостраничных / форм-данных проблема возникает из-за уродливой головы.

Я изо всех сил пытаюсь понять, что может быть проблемой, или даже где искать решение. Я новичок в Puma, поэтому не знаю, не хватает ли у меня что-то важное в моей конфигурации, чтобы справиться с этим. Любые идеи очень приветствуются.

Запрос iOS (отлично работает)

POST /api/v2/attachments HTTP/1.1 Host: <omitted for security> Accept: */* Proxy-Connection: keep-alive X-Requested-With: XMLHttpRequest Accept-Encoding: gzip, deflate If-None-Match: "2146f2b315668b29682ec01973ae4155" Accept-Language: en-us Content-Type: multipart/form-data; boundary=+++++org.apache.cordova.formBoundary Content-Length: 13794 User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13B143 (4954749952) Connection: keep-alive Cookie: XSRF-TOKEN=<omitted for security>; _session_id=<omitted for security> --+++++org.apache.cordova.formBoundary Content-Disposition: form-data; name="type" Image --+++++org.apache.cordova.formBoundary Content-Disposition: form-data; name="attachment[attachment]"; filename="cdv_photo_003.jpg" Content-Type: image/jpeg Content-Length: 13466 iُ B~," #BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz """"""''''',,,,,,,,,, ................................................... (<a$GW_ЬUպi&dA +x=R ? )uH v "ԊdcpIސW̚<<Kx~E1x EN]M% >r{ Y_Yc<jʥYH_2r[P['6 d#wS+5Bfu <more Binary data omitted for brevity> --+++++org.apache.cordova.formBoundary-- 

Эквивалентный запрос на Android (вызывает ошибку)

 POST /api/v2/attachments HTTP/1.1 Content-Type: multipart/form-data; boundary=+++++ Cookie: _session_id=<omitted for security>; XSRF-TOKEN=<omitted for security> Transfer-Encoding: chunked User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.0; SM-G900F Build/LRX21T) Host: <omitted for security> Connection: Keep-Alive Accept-Encoding: gzip --+++++ Content-Disposition: form-data; name="type" Image --+++++ Content-Disposition: form-data; name="attachment[attachment]"; filename="modified.jpg?1446611766814" Content-Type: image/jpeg Exif (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egc" #BR$3br <more Binary data omitted for brevity> --+++++-- 

В этих запросах есть некоторые очевидные различия, и, хотя я не стал бы называть себя экспертом по протоколу HTTP, ни одно из различий для меня не похоже, как на то, что может привести к тому, что Puma упадет.

Трассировка ошибок стойки:

 2015-11-04T04:36:07.041124+00:00 app[web.1]: 2015-11-04 04:36:07 +0000: Rack app error: #<EOFError: bad content body> 2015-11-04T04:36:07.041131+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/multipart/parser.rb:74:in `block in fast_forward_to_first_boundary' 2015-11-04T04:36:07.041133+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/multipart/parser.rb:72:in `loop' 2015-11-04T04:36:07.041134+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/multipart/parser.rb:72:in `fast_forward_to_first_boundary' 2015-11-04T04:36:07.041152+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/multipart/parser.rb:15:in `parse' 2015-11-04T04:36:07.041153+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/multipart.rb:25:in `parse_multipart' 2015-11-04T04:36:07.041154+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/request.rb:377:in `parse_multipart' 2015-11-04T04:36:07.041154+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/request.rb:203:in `POST' 2015-11-04T04:36:07.041155+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/methodoverride.rb:26:in `method_override' 2015-11-04T04:36:07.041156+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/methodoverride.rb:14:in `call' 2015-11-04T04:36:07.041157+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041157+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/runtime.rb:17:in `call' 2015-11-04T04:36:07.041158+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041158+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-rewrite-1.5.0/lib/rack/rewrite.rb:24:in `call' 2015-11-04T04:36:07.041159+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041159+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/activesupport-4.0.5/lib/active_support/cache/strategy/local_cache.rb:83:in `call' 2015-11-04T04:36:07.041160+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041160+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/actionpack-4.0.5/lib/action_dispatch/middleware/static.rb:64:in `call' 2015-11-04T04:36:07.041161+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041161+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/rack-1.5.2/lib/rack/sendfile.rb:112:in `call' 2015-11-04T04:36:07.041161+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041162+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/railties-4.0.5/lib/rails/engine.rb:511:in `call' 2015-11-04T04:36:07.041162+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/railties-4.0.5/lib/rails/application.rb:97:in `call' 2015-11-04T04:36:07.041163+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/railties-4.0.5/lib/rails/railtie/configurable.rb:30:in `method_missing' 2015-11-04T04:36:07.041163+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/newrelic_rpm-3.13.2.302/lib/new_relic/agent/instrumentation/middleware_tracing.rb:67:in `call' 2015-11-04T04:36:07.041163+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/configuration.rb:78:in `call' 2015-11-04T04:36:07.041164+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/server.rb:541:in `handle_request' 2015-11-04T04:36:07.041164+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/server.rb:388:in `process_client' 2015-11-04T04:36:07.041165+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/server.rb:270:in `block in run' 2015-11-04T04:36:07.041165+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/thread_pool.rb:106:in `call' 2015-11-04T04:36:07.041165+00:00 app[web.1]: /app/vendor/bundle/ruby/2.1.0/gems/puma-2.14.0/lib/puma/thread_pool.rb:106:in `block in spawn_thread' 2015-11-04T04:36:07.523877+00:00 heroku[router]: sock=backend at=error code=H18 desc="Server Request Interrupted" method=POST path="/api/v2/attachments" host=<omitted for security> request_id=a5958dab-8c7c-4121-acc3-54d28be9ad4c fwd="<omitted for security>" dyno=web.1 connect=2ms service=484ms status=503 bytes=154 

Вы должны Rack::Multipart::Parser.fast_forward_to_first_boundary обезьяну Rack::Multipart::Parser.fast_forward_to_first_boundary чтобы добавить трассировку журнала и подтвердить, что содержимое, переданное парсеру, пуст, когда оно достигает этой функции (то есть StringIO , на StringIO ссылается @env['rack.input'] многократном разборе не возвращает данных).

Для получения дополнительной информации: rack-throwing-eoferror-bad-content-body

Я не смог удовлетворительно решить эту проблему, источником проблемы является то, как Puma обрабатывает данные multipart / chunked. Поскольку мы не используем самую последнюю версию, и в настоящее время ее нелегко обновить, я «взломал» решение, как показано ниже.

Странная вещь здесь, кажется, что это недостающий заголовок Content-Length, который вызывает проблему, – несмотря на то, что спецификация HTTP заявляет, что chunked-запросы не должны иметь этот заголовок.

Я предполагаю (хотя я мог ошибаться), что для реализации SSL требуется Content-Length, что противоречит требованию, чтобы chunked-запросы не предоставляли его.

На данный момент решение состоит в том, чтобы отключить фрагментированные данные из приложения cordova. Мы используем плагин cordova-file-transfer-plugin, который позволяет установить chunked на false.

Не идеально, но он работает. Из https://github.com/apache/cordova-plugin-file-transfer

  options.chunkedMode = false; 

Вы должны проверить, что говорит журнал сервера об этой ошибке 500