代码之家  ›  专栏  ›  技术社区  ›  Ben Scheirman

Rack Rack.输入变量被截断?

  •  7
  • Ben Scheirman  · 技术社区  · 14 年前

    我编写了一个框架中间件来自动解压压缩请求体。代码似乎工作得很好,但是当我将它插入Rails应用程序时,我从actionController::ParamSparser得到一个“无效JSON”的失败消息。

    作为一种调试机制,我将压缩内容和解压缩内容都写入一个文件(以确保代码正常工作),并且我确实收到了我的原始JSON文档(在客户机压缩之前)。

    我要发布的数据 JSON数据,未压缩的内容被检测为有效的JSON来自 http://jsonlint.com .

    知道我做错了什么吗?

    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
    

    下面是我从控制台得到的错误:

    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'
    

    最后一条信息,我将在actionController::failsafe之后插入这个中间件。

    编辑:看起来这不是截断问题

    经过更多的挖掘,看起来这根本不是一个截断问题。日志只是剪切输出,因此 像是截断问题。

    现在我不知道为什么JSON会变成无效的。我需要手动逃跑吗?

    1 回复  |  直到 14 年前
        1
  •  16
  •   Aaron Lerch    14 年前

    我绝对不是红宝石专家。为了验证我的结果,我也没有尝试重新处理这个问题。但是在深入研究了框架和actionpack代码之后,我可能会得到一些东西。

    “rack.input”文档的状态为: “输入流是一个类似IO的对象,其中包含原始HTTP Post数据。”

    所以你使用得很正确,看起来。

    但是,actionpack尝试从主体中解析json(如果将内容类型指定为json),并像这样检索主体:

    when :json
        body = request.raw_post
    

    其中“request”是actionpack自己的请求类,而“raw-post”的定义如下:

    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
    

    “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
    

    这一切看起来都很好(尽管要弄清楚谁先缓存值会让人困惑:)。 问题似乎在于如何读取POST数据:

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

    所以我猜问题是,既然你改变了“rack.input”,但不更新“content-length”,actionpack就会截断数据,因为显然压缩后的内容会比未压缩的内容短。

    尝试更新中间件代码中的“内容长度”,看看是否修复了它。