2010-10-18 16 views
7

Ho scritto un pezzo di Rack Middleware per decomprimere automaticamente i corpi delle richieste compresse. Il codice sembra funzionare bene, ma quando lo collego alla mia app per i rails, ricevo un errore "Invalid JSON" da ActionController :: ParamsParser.Rack rack. La variabile di input viene troncata?

Come meccanismo di debug, sto scrivendo sia il contenuto zippato, sia il contenuto decompresso in un file (per assicurarmi che il codice funzioni correttamente) e ricevo il mio documento JSON originale (prima che il client lo comprima).

I dati che sto postando sono dati JSON e il contenuto decompresso viene rilevato come JSON valido da http://jsonlint.com.

Qualche idea su cosa sto facendo male?

class CompressedRequests 
    def initialize(app) 
    @app = app 
    end 

    def call(env) 
    input = env['rack.input'].read 

    #output the zipped data we received 
    File.open('/Users/ben/Desktop/data.gz', 'w+') do |f| 
     f.write input 
    end 

    if env['REQUEST_METHOD'] =~ /(POST|PUT)/ 
     if env.keys.include? 'HTTP_CONTENT_ENCODING' 
     new_input = decode(input, env['HTTP_CONTENT_ENCODING']) 
     env['rack.input'] = StringIO.new(new_input) 

     #output our decoded data (for debugging) 
     File.open('/Users/ben/Desktop/data.txt', 'w+') do |f| 
      f.write env['rack.input'].read 
     end 

     env.delete('HTTP_CONTENT_ENCODING') 
     end 
    end 

    env['rack.input'].rewind 
    status, headers, response = @app.call(env) 
    return [status, headers, response] 
    end 

    def decode(input, content_encoding) 
    if content_encoding == 'gzip' 
     Zlib::GzipReader.new(input).read 
    elsif content_encoding == 'deflate' 
     Zlib::Inflate.new.inflate new input 
    else 
     input 
    end 
    end 
end 

Qui è l'errore che sto ricevendo dalla console:

Contents::"2010-05-17T12:46:30Z","background":false},{"longitude":-95.38620785000001,"latitude":29.62815358333334,"estimated_speed":14.04305,"timestamp":"2010-05-17T12:46:36Z","background":false},{"longitude":-95.3862767,"latitude":29.62926725,"estimated_speed":39.87791,"timestamp":"2010-05-17T12:46:42Z","background":false},{"longitude":-95.38655023333334,"latitude":29.63051011666666,"estimated_speed":46.09239,"timestamp":"2010-05-17T12:46:49Z","background":false},{"longitude":-95.38676226666666,"latitude":29.63158775,"estimated_speed":47.34936,"timestamp":"2010-05-17T12:46:55Z","background":false},{"longitude":-95.38675346666666,"latitude":29.63219841666666,"estimated_speed":22.54016,"timestamp":"2010-05-17T12:47:03Z","background":false},{"longitude":-95.38675491666666,"latitude":29.63265714999999,"estimated_speed":14.03642,"timestamp":"2010-05-17T12:47:10Z","background":false},{"longitude":-95.38677551666666,"latitude":29.63358661666667,"estimated_speed":29.29489,"timestamp":"2010-05-17T12:47:17Z","background":false},{"longitude":-95.38679026666662,"latitude":29.63466445,"estimated_speed":38.34926,"timestamp":"2010-05-17T12:47:24Z","background":false},{"longitude":-95.38681656666668,"latitude":29.63590941666666,"estimated_speed":44.82093,"timestamp":"2010-05-17T12:47:31Z","background":false},{"longitude":-95.38683366666667,"latitude":29.63679638333334,"estimated_speed":40.21729,"timestamp":"2010-05-17T12:47:37Z","background":false},{"longitude":-95.38685133333333,"latitude":29.63815714999999,"estimated_speed":44.86543,"timestamp":"2010-05-17T12:47:44Z","background":false},{"longitude":-95.3868655 
/!\ FAILSAFE /!\ Mon Oct 18 18:18:43 -0500 2010 
    Status: 500 Internal Server Error 
    Invalid JSON string 
    /Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/backends/yaml.rb:14:in `decode' 
    /Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/decoding.rb:11:in `__send__' 
    /Library/Ruby/Gems/1.8/gems/activesupport-2.3.5/lib/active_support/json/decoding.rb:11:in `decode' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/params_parser.rb:42:in `parse_formatted_parameters' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/params_parser.rb:11:in `call' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/session/cookie_store.rb:93:in `call' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/failsafe.rb:26:in `call' 
    /Users/ben/projects/safecell/safecellweb/lib/compressed_requests.rb:36:in `call' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `call' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `synchronize' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/lock.rb:11:in `call' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/dispatcher.rb:114:in `call' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/reloader.rb:34:in `run' 
    /Library/Ruby/Gems/1.8/gems/actionpack-2.3.5/lib/action_controller/dispatcher.rb:108:in `call' 
    /Library/Ruby/Gems/1.8/gems/rails-2.3.5/lib/rails/rack/static.rb:31:in `call' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:46:in `call' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:40:in `each' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/urlmap.rb:40:in `call' 
    /Library/Ruby/Gems/1.8/gems/rails-2.3.5/lib/rails/rack/log_tailer.rb:17:in `call' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/content_length.rb:13:in `call' 
    /Library/Ruby/Gems/1.8/gems/rack-1.0.1/lib/rack/handler/webrick.rb:50:in `service' 

Un ultimo pezzo di informazione, sto inserendo questa middleware dopo ActionController :: fail-safe.

EDIT: Sembra che non è un problema di troncamento

Dopo più di scavo, sembra che non è un problema di troncamento, dopo tutto. I log semplicemente ritagliano l'output in modo che l'aspetto corrisponda a come un problema di troncamento.

A questo punto non sono sicuro del perché JSON non sia valido. Devo eseguire qualche operazione di escape manuale?

risposta

15

Non sono un esperto di rubini da nessuna parte. Inoltre, non ho provato a riprodurre questo problema per verificare i miei risultati. Ma dopo aver scavato attraverso il rack e il codice del pacchetto, potrei avere qualcosa.

Il doc per "rack.input" afferma: "Il flusso di input è un oggetto simile all'IO che contiene i dati POST HTTP non elaborati."

Quindi lo stai usando correttamente, sembrerebbe.

Tuttavia, actionpack tenta di analizzare JSON fuori dal corpo (se il tipo di contenuto è specificato come JSON) e recupera il corpo come questo:

when :json 
    body = request.raw_post 

dove "richiesta" è propria classe Richiesta di actionpack e "raw_post" è definito in questo modo:

def raw_post 
    unless @env.include? 'RAW_POST_DATA' 
    @env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i) 
    body.rewind if body.respond_to?(:rewind) 
    end 
    @env['RAW_POST_DATA'] 
end 

e "Request.body" è:

def body 
    if raw_post = @env['RAW_POST_DATA'] 
    raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) 
    StringIO.new(raw_post) 
    else 
    @env['rack.input'] 
    end 
end 

Tutto sembra a posto e buono (anche se è complicato capire chi memorizza prima il valore :)). Sembra che il problema è nel modo in cui i dati POST viene letto:

@env['RAW_POST_DATA'] = body.read(@env['CONTENT_LENGTH'].to_i) 

Così sto cercando di indovinare il problema è che da quando si cambia "rack.input", ma non si aggiorna "CONTENT_LENGTH", actionpack è troncare i dati poiché ovviamente il contenuto compresso sarebbe stato più breve del contenuto decompresso.

provare ad aggiornare "CONTENT_LENGTH" nel codice middleware e vedere se lo ripara.

+1

Amico è rock. Era così! Ecco il codice di lavoro aggiornato: http://gist.github.com/635471 –

+1

cool story bro: D – noodl

+0

Excelent spiegazione, così che ognuno dovrebbe essere :) – Pablo

Problemi correlati