2010-10-18 16 views
7

Tôi đã viết một phần Rack Middleware để tự động giải nén các thân yêu cầu nén. Mã này có vẻ là làm việc tốt, nhưng khi tôi cắm nó vào ứng dụng đường ray của tôi, tôi nhận được một thất bại "JSON không hợp lệ" từ ActionController :: ParamsParser.Giá rack.input biến bị cắt ngắn?

Là cơ chế gỡ lỗi, tôi viết cả nội dung nén và nội dung chưa giải nén vào tệp (để đảm bảo mã hoạt động đúng) và tôi nhận tài liệu JSON gốc của mình (trước khi khách hàng nén tài liệu đó lên)).

Dữ liệu tôi đang đăng Dữ liệu JSON và nội dung được giải nén được phát hiện dưới dạng JSON hợp lệ từ http://jsonlint.com.

Bất kỳ ý tưởng nào tôi đang làm sai?

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 

Đây là lỗi mà tôi nhận được từ giao diện điều khiển:

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' 

Một mảnh cuối cùng của thông tin, tôi đang chèn middleware này sau khi ActionController :: Failsafe.

EDIT: Có vẻ như nó không phải là một vấn đề cắt ngắn

Sau khi đào sâu thêm, có vẻ như nó không phải là một vấn đề cắt ngắn sau khi tất cả. Các bản ghi chỉ đơn giản là cắt đầu ra sao cho nó trông như một sự cố cắt ngắn.

Tại thời điểm này, tôi không chắc tại sao JSON lại đến không hợp lệ. Tôi có cần thực hiện bất kỳ thao tác thoát bằng tay nào không?

Trả lời

15

Tôi không phải là chuyên gia ruby ​​theo bất kỳ khoảng thời gian nào. Tôi cũng đã không cố gắng để repro vấn đề này để xác minh kết quả của tôi. Nhưng sau khi đào thông qua các rack và mã actionpack, tôi có thể có một cái gì đó.

Tài liệu cho "rack.input" nêu rõ: "Luồng đầu vào là một đối tượng giống như IO chứa dữ liệu HTTP POST thô".

Vì vậy, bạn đang sử dụng chính xác, có vẻ như vậy.

Tuy nhiên, actionpack cố gắng phân tích cú pháp JSON ra khỏi cơ thể (nếu kiểu nội dung được quy định như JSON) và lấy cơ thể như thế này:

when :json 
    body = request.raw_post 

nơi "yêu cầu" là lớp Yêu cầu riêng actionpack, và "raw_post" được định nghĩa như thế này:

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 

và "Request.body" là:

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 

Điều đó có vẻ tốt và tốt (mặc dù nó khó hiểu để tìm ra người lưu trữ giá trị đầu tiên :)). Dường như vấn đề là ở cách dữ liệu bài được đọc:

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

Vì vậy, tôi đoán vấn đề là kể từ khi bạn thay đổi "rack.input" nhưng không cập nhật "CONTENT_LENGTH", actionpack là cắt ngắn dữ liệu vì rõ ràng nội dung được nén sẽ ngắn hơn nội dung đã giải nén.

Thử cập nhật "CONTENT_LENGTH" trong mã phần mềm trung gian của bạn và xem có khắc phục được không.

+1

Anh bạn đá. Điều đó là vậy đó! Đây là mã làm việc được cập nhật: http://gist.github.com/635471 –

+1

Câu chuyện thú vị bro: D – noodl

+0

Giải thích bằng Excel, cách mọi người nên là :) – Pablo