2013-04-02 8 views
10

Unsere Ruby-on-Rails-Site verfügt über einen URI, an den einer unserer Partner XML-Daten anmeldet.Rails/Rack: "ArgumentError: ungültige% -Encodierung" für POST-Daten

Da wir nicht mit XML arbeiten wollen, stopfen wir die Rohdaten buchstäblich in eine Datenbankspalte und gehen nicht weiter mit der Verarbeitung.

jedoch einen der Pfosten gaben wir uns in airbrake diesen Fehler erhalten:

ArgumentError: invalid %-encoding ("http://ns.hr-xml.org/2004-08-02" 
userId="" password=""><BackgroundReportPackage type="report"> 
<ProviderReferenceId>.... 

mit 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>' 

Das Problem ist, dass der POST die Daten enthält:

<ChargeOrComplaint>DRIVE WHILE BLOOD ALCOHOL LEVEL IS 0.08% OR MORE</ChargeOrComplaint> 

Vermutlich ist dies gültige XML, aber die nackte % am Ende 0.08% verursacht den Fehler, da es über HTTP kommt und ich vermute Rack erwartet, dass es URL-codiert wird.

Das Backtrace zeigt an, dass dies geschieht, bevor es überhaupt zu unserem Code kommt, also glaube ich nicht, dass es irgendetwas damit zu tun hat, wie wir es verarbeiten.

Meine Fragen, dann:

1) Woher kommt das Problem liegen? Ruby 1.9.3 Implementierung von decode_www_form_component (an der Spitze des Stack-Trace)? Gestell? POST-Daten oder Header unseres Partners? Unser Umgang mit dem POST?

2) Müssen über HTTP gepostete XML-Daten URL-codiert werden?

3) Gibt es eine Kopfzeile, die dieser POST benötigt, damit Rack sie richtig interpretiert? (Das heißt, dass es XML-Binärdaten sind und nicht URL-codiert).

4) Wenn ich unseren Partner nicht dazu bringen kann, das zu ändern, was er an uns sendet, wie könnten wir das umgehen? Einige Rack-Middleware?

+2

Ich renne in genau dieses Problem. Hast du am Ende eine Lösung gefunden? –

Antwort

7

Ich vermute, dass Ihr Partner die Daten wahrscheinlich als "x-www-form-urlencoded" an Sie sendet, was Rack dazu bringt, es so zu analysieren. Wenn sie das, was sie senden, ändern können, vermute ich, dass ihr Inhaltstyp "text/xml" dies beheben wird.

Wenn Sie sie nicht dazu bringen können, das zu ändern, was sie senden, dann denke ich, dass Sie Rack-Middleware (oder monkeypatching) verwenden müssen. Obwohl Sie in der Rack-Quelle herumstöbern könnten, gibt es vielleicht eine Einstellung, die es vermeidet, Parsing durchzuführen.

+4

Ich denke Rack geht auch davon aus, dass die Anfrage 'x-www-form-urlencoded' ist, wenn es ein POST ist und es keinen Content-Type-Header gibt. Dies ist das Verhalten, das Sie sehen, wenn der Partner keinen Inhalt angibt Geben Sie Folgendes ein: https://github.com/rack/rack/blob/1.4.5/lib/rack/request.rb#L171 – matt

0

In meinem Fall war der Grund zusätzliche Zeilenumbrüche nach den Headern und vor dem Anfragetext. Ich vermute, dass es eine Content-Length-Inkonsistenz gibt, die den Parser auslöst. Wenn Sie Header programmgesteuert festlegen, stellen Sie sicher, dass sie keine abschließenden Zeilenumbrüche enthalten.

1

Es gibt einige Diskussionen in verschiedenen Foren, wo die Verantwortung liegt für das Erfassen ungültiger Codierungsfehler im Inhalt des Anfragetextes, aber weder Rack noch Rails behandeln es, beide lassen es zur App übergehen.Um ungültiges% -encoding in POST-Daten in meiner Anwendung zu arbeiten, habe ich eine ähnliche Lösung für diese Frage im Zusammenhang: Rails ArgumentError: invalid %-encoding

ich diese Middleware in app/middleware/invalid_post_data_interceptor.rb hinzugefügt ungültige Post-Daten abfangen:

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 

Dann fügte es zum Middleware-Stack durch Hinzufügen dieser zu application.rb:

config.middleware.insert_before Rack::Runtime, "InvalidPostDataInterceptor"