2013-04-02 12 views
10

Il nostro sito ruby ​​on rails ha un URI a cui uno dei nostri partner invia dati XML.Rails/Rack: "ArgumentError: invalid% -encoding" per i dati POST

Dato che non vogliamo occuparci di XML, inseriamo letteralmente i dati grezzi in una colonna di database e non procediamo oltre con l'elaborazione.

Tuttavia, uno dei posti che abbiamo ricevuto ci ha dato questo errore in airbrake:

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

Il problema è che il POST contiene i dati:

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

Presumibilmente questo è XML valido, ma il % nudo alla fine di 0.08% sta causando l'errore, dal momento che sta arrivando via HTTP e immagino che il rack si aspetti che sia codificato come URL.

Il backtrace indica che questo sta accadendo prima che arrivi anche al nostro codice, quindi non penso che abbia nulla a che fare con come lo stiamo elaborando.

mie domande, allora:

1) Da dove viene la menzogna problema? L'implementazione di Ruby 1.9.3 di decode_www_form_component (nella parte superiore della traccia dello stack)? Rack? Dati o intestazioni POST del nostro partner? La nostra gestione del POST?

2) I dati XML inviati tramite HTTP devono essere codificati tramite URL?

3) Esiste un'intestazione che questo POST deve avere per essere interpretato correttamente da Rack? (vale a dire che si tratta di dati binari XML e non di URL codificati).

4) Se non riesco a convincere il nostro partner a cambiare ciò che ci stanno postando, come possiamo aggirare il problema? Alcuni middleware Rack?

+2

Sto correndo in questo esatto problema. Hai finito per trovare una soluzione? –

risposta

7

Immagino che il tuo partner stia probabilmente inviando i dati come "x-www-form-urlencoded", facendo in modo che Rack provi ad analizzarlo in quel modo. Se possono cambiare ciò che stanno inviando, sospetto che il loro tipo di contenuto "text/xml" risolverà questo problema.

Se non riesci a far loro cambiare ciò che inviano, allora sì, penso che dovresti usare il middleware Rack (o monkeypatching). Sebbene tu possa sondare la fonte del Rack, forse c'è un'impostazione per evitare di fare l'analisi.

+4

Penso che Rack supponga anche che la richiesta sia 'x-www-form-urlencoded' se è un POST e non c'è un'intestazione Content-Type, quindi questo è il comportamento che si vedrebbe se il partner non specifica alcun contenuto digita: https://github.com/rack/rack/blob/1.4.5/lib/rack/request.rb#L171 – matt

0

Nel mio caso il motivo è stato un nuovo extra riga dopo le intestazioni e prima del corpo della richiesta. Sto indovinando che c'è incoerenza Content-Length che butta via il parser. Se si impostano le intestazioni a livello di codice, assicurarsi che non abbiano le nuove righe finali.

1

Ci sono alcuni dibattiti in vari forum su dove si trova la responsabilità di rilevare errori di codifica non validi nel contenuto del corpo della richiesta, ma né il rack né i binari lo gestiscono, sia lasciandolo all'app da gestire.Per ovviare a non valida% -encoding nei dati POST nella mia app, ho usato una soluzione simile a questa domanda correlata: Rails ArgumentError: invalid %-encoding

ho aggiunto questo middleware in app/middleware/invalid_post_data_interceptor.rb di intercettare i dati non validi postale:

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 

Poi ha aggiunto alla pila middleware aggiungendo questo a application.rb:

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