ruby on rails 3 - Rails/Rack: "ArgumentError: invalid-encoding" para datos POST
ruby-on-rails-3 ruby-1.9.3 (3)
Nuestro sitio Ruby on Rails tiene un URI al que uno de nuestros socios envía datos XML.
Como no queremos tratar con XML, literalmente simplemente rellenamos los datos brutos en una columna de base de datos y no avanzamos en su procesamiento.
Sin embargo, una de las publicaciones que recibimos nos dio este error en el freno de aire:
ArgumentError: invalid %-encoding ("http://ns.hr-xml.org/2004-08-02"
userId="" password=""><BackgroundReportPackage type="report">
<ProviderReferenceId>....
Con backtrace:
vendor/ruby-1.9.3/lib/ruby/1.9.1/uri/common.rb:898:in `decode_www_form_component''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:41:in `unescape''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:94:in `block (2 levels) in parse_nested_query''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:94:in `map''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:94:in `block in parse_nested_query''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:93:in `each''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/utils.rb:93:in `parse_nested_query''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/request.rb:332:in `parse_query''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/request.rb:209:in `POST''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/methodoverride.rb:26:in `method_override''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/methodoverride.rb:14:in `call''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/runtime.rb:17:in `call''
vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/cache/strategy/local_cache.rb:72:in `call''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/lock.rb:15:in `call''
vendor/bundle/ruby/1.9.1/gems/actionpack-3.2.13/lib/action_dispatch/middleware/static.rb:63:in `call''
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:136:in `forward''
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:143:in `pass''
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:155:in `invalidate''
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:71:in `call!''
vendor/bundle/ruby/1.9.1/gems/rack-cache-1.2/lib/rack/cache/context.rb:51:in `call''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/engine.rb:479:in `call''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/application.rb:223:in `call''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/railtie/configurable.rb:30:in `method_missing''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/deflater.rb:13:in `call''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/content_length.rb:14:in `call''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/rack/log_tailer.rb:17:in `call''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:80:in `block in pre_process''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:78:in `catch''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:78:in `pre_process''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:53:in `process''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/connection.rb:38:in `receive_data''
vendor/bundle/ruby/1.9.1/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run_machine''
vendor/bundle/ruby/1.9.1/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in `run''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/backends/base.rb:63:in `start''
vendor/bundle/ruby/1.9.1/gems/thin-1.4.1/lib/thin/server.rb:159:in `start''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/handler/thin.rb:13:in `run''
vendor/bundle/ruby/1.9.1/gems/rack-1.4.5/lib/rack/server.rb:268:in `start''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands/server.rb:70:in `start''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:55:in `block in <top (required)>''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:50:in `tap''
vendor/bundle/ruby/1.9.1/gems/railties-3.2.13/lib/rails/commands.rb:50:in `<top (required)>''
script/rails:6:in `require''
script/rails:6:in `<main>''
El problema es que el POST contiene los datos:
<ChargeOrComplaint>DRIVE WHILE BLOOD ALCOHOL LEVEL IS 0.08% OR MORE</ChargeOrComplaint>
Es de suponer que este es un XML válido, pero el %
desnudo al final de 0.08%
está causando el error, ya que viene a través de HTTP y supongo que Rack espera que esté codificado en la URL.
La traza inversa indica que esto está sucediendo incluso antes de que llegue a nuestro código, por lo que no creo que tenga nada que ver con la forma en que lo estamos procesando.
Mis preguntas, entonces:
1) ¿Dónde está el problema? ¿Implementación de Ruby 1.9.3 de decode_www_form_component
(en la parte superior del seguimiento de la pila)? ¿Estante? Los datos POST de nuestra pareja o los encabezados? ¿Nuestro manejo del POST?
2) ¿Los datos XML PUBLICADOS a través de HTTP necesitan ser URL codificados?
3) ¿Hay un encabezado que este POST necesita tener para que Rack lo interprete correctamente? (es decir, que se trata de datos binarios XML y no de codificación URL).
4) Si no puedo lograr que nuestro socio cambie lo que nos están enviando, ¿cómo podríamos solucionarlo? ¿Algún middleware Rack?
Supongo que su pareja probablemente le envíe los datos como "x-www-form-urlencoded", por lo que Rack intentará analizarlos de esa manera. Si pueden cambiar lo que envían, sospecho que su tipo de contenido "text / xml" solucionará esto.
Si no puedes hacer que cambien lo que envían, entonces sí, creo que tendrías que usar el middleware Rack (o monopatching). Aunque podría hurgar en la fuente Rack, tal vez haya una configuración para evitar hacer cualquier análisis sintáctico.
En mi caso, el motivo eran nuevas líneas adicionales después de los encabezados y antes del cuerpo de la solicitud. Supongo que hay inconsistencia en la longitud del contenido que arroja el analizador. Si está configurando encabezados programáticamente, asegúrese de que no tengan líneas nuevas.
Existe cierto debate en varios foros sobre dónde recae la responsabilidad de detectar errores de codificación no válidos en el contenido del cuerpo de la solicitud, pero ni el estante ni los rieles lo manejan, ambos dejan que la aplicación lo maneje. Para evitar la codificación de% no válida en los datos POST de mi aplicación, utilicé una solución similar a esta pregunta relacionada: Rails ArgumentError: invalid -encoding no válido
app/middleware/invalid_post_data_interceptor.rb
este middleware en la app/middleware/invalid_post_data_interceptor.rb
para interceptar los datos de publicación no válidos:
class InvalidPostDataInterceptor
def initialize(app)
@app = app
end
def call(env)
request_content = Rack::Request.new(env).POST rescue :bad_form_data
headers = {''Content-Type'' => ''text/plain''}
if request_content == :bad_form_data
[400, headers, [''Bad Request'']]
else
@app.call(env)
end
end
end
Luego lo agregué a la pila de middleware agregando esto a application.rb
:
config.middleware.insert_before Rack::Runtime, "InvalidPostDataInterceptor"