2010-10-18 15 views
7

Ich schrieb ein Stück Rack Middleware, um komprimierte Anfragekörper automatisch zu entpacken. Der Code scheint gut zu funktionieren, aber wenn ich ihn in meine Rails-App einstecke, bekomme ich einen Fehler "Invalid JSON" von ActionController :: ParamsParser.Rack rack.input Variable wird abgeschnitten?

Als Debugging-Mechanismus schreibe ich sowohl den gezippten Inhalt als auch den entpackten Inhalt in eine Datei (um sicherzustellen, dass der Code ordnungsgemäß funktioniert) und erhalte mein ursprüngliches JSON-Dokument (bevor der Client es zippt)).

Die Daten, die ich schreibe ist JSON-Daten, und der entpackte Inhalt wird als gültige JSON von http://jsonlint.com erkannt.

Irgendwelche Ideen, was ich falsch mache?

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 

Hier ist der Fehler, den ich von der Konsole bekommen:

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' 

Ein letztes Stück Information, ich bin das Einfügen dieser Middleware nach Action :: Failsafe.

EDIT: Sieht aus wie es kein Abschneiden Problem ist

Nach mehr zu graben, es sieht aus wie es schließlich kein Abschneiden Problem. Die Protokolle werden einfach abgeschnitten die Ausgabe, so dass es aussieht wie ein Kürzungsproblem.

An diesem Punkt bin ich nicht sicher, warum der JSON als ungültig kommt. Muss ich manuell fliehen?

Antwort

15

Ich bin kein Ruby-Experte in keiner Hinsicht. Ich habe auch nicht versucht, dieses Problem zu wiederholen, um meine Ergebnisse zu überprüfen. Aber nach dem Durchstöbern des Rack- und Actionpack-Codes könnte ich etwas haben.

Das Dokument für "rack.input" Status: "Der Eingabestream ist ein IO-ähnliches Objekt, das die HTTP-POST-Rohdaten enthält."

Sie verwenden das also richtig, so scheint es.

jedoch versucht Action JSON parsen aus dem Körper (wenn der Inhaltstyp als JSON angegeben ist) und ruft den Körper wie folgt aus:

when :json 
    body = request.raw_post 

wo „Anfrage“ Action eigenen Antrag Klasse ist, und "raw_post" wie folgt definiert ist:

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 

und "Request.body" ist:

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 

Das sieht alles gut und gut aus (obwohl es verwirrend ist herauszufinden, wer den Wert zuerst speichert). Es ist wie das Problem aussieht, ist in, wie die Post-Daten gelesen werden:

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

Ich vermute, das Problem ist also, dass Sie da „rack.input“ ändern, aber nicht aktualisieren „CONTENT_LENGTH“, Action ist Abschneiden der Daten, da der komprimierte Inhalt offensichtlich kürzer als der entpackte Inhalt gewesen wäre.

Versuchen Sie, "CONTENT_LENGTH" in Ihrem Middleware-Code zu aktualisieren, um zu sehen, ob das Problem behoben ist.

+1

Dude Rock. Das war's! Hier ist der aktualisierte Arbeitscode: http://gist.github.com/635471 –

+1

Coole Geschichte bro: D – noodl

+0

Exzellente Erklärung, dass wie jeder sein sollte :) – Pablo