rest-core 3.6.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/.travis.yml +0 -1
- data/CHANGES.md +28 -0
- data/Gemfile +0 -2
- data/README.md +14 -95
- data/Rakefile +16 -3
- data/example/simple.rb +1 -0
- data/example/use-cases.rb +15 -3
- data/lib/rest-core.rb +38 -75
- data/lib/rest-core/client/universal.rb +3 -1
- data/lib/rest-core/client_oauth1.rb +64 -59
- data/lib/rest-core/event.rb +9 -11
- data/lib/rest-core/middleware/auth_basic.rb +21 -21
- data/lib/rest-core/middleware/bypass.rb +8 -8
- data/lib/rest-core/middleware/cache.rb +94 -90
- data/lib/rest-core/middleware/clash_response.rb +15 -14
- data/lib/rest-core/middleware/common_logger.rb +27 -26
- data/lib/rest-core/middleware/default_headers.rb +8 -8
- data/lib/rest-core/middleware/default_payload.rb +8 -8
- data/lib/rest-core/middleware/default_query.rb +8 -8
- data/lib/rest-core/middleware/default_site.rb +12 -12
- data/lib/rest-core/middleware/defaults.rb +38 -38
- data/lib/rest-core/middleware/error_detector.rb +10 -10
- data/lib/rest-core/middleware/error_detector_http.rb +6 -4
- data/lib/rest-core/middleware/error_handler.rb +14 -14
- data/lib/rest-core/middleware/follow_redirect.rb +28 -27
- data/lib/rest-core/middleware/json_request.rb +13 -11
- data/lib/rest-core/middleware/json_response.rb +29 -28
- data/lib/rest-core/middleware/oauth1_header.rb +84 -83
- data/lib/rest-core/middleware/oauth2_header.rb +27 -25
- data/lib/rest-core/middleware/oauth2_query.rb +15 -15
- data/lib/rest-core/middleware/query_response.rb +14 -14
- data/lib/rest-core/middleware/retry.rb +25 -23
- data/lib/rest-core/middleware/smash_response.rb +15 -14
- data/lib/rest-core/middleware/timeout.rb +18 -19
- data/lib/rest-core/test.rb +1 -18
- data/lib/rest-core/util/clash.rb +38 -37
- data/lib/rest-core/util/config.rb +40 -39
- data/lib/rest-core/util/dalli_extension.rb +11 -10
- data/lib/rest-core/util/hmac.rb +9 -8
- data/lib/rest-core/util/json.rb +55 -54
- data/lib/rest-core/util/parse_link.rb +13 -12
- data/lib/rest-core/util/parse_query.rb +24 -22
- data/lib/rest-core/version.rb +1 -1
- data/rest-core.gemspec +121 -158
- data/test/test_cache.rb +2 -0
- data/test/test_default_payload.rb +1 -1
- data/test/test_error_handler.rb +5 -4
- data/test/test_timeout.rb +9 -8
- data/test/test_universal.rb +8 -0
- metadata +9 -73
- data/lib/rest-core/builder.rb +0 -162
- data/lib/rest-core/client.rb +0 -277
- data/lib/rest-core/client/simple.rb +0 -2
- data/lib/rest-core/engine.rb +0 -36
- data/lib/rest-core/engine/dry.rb +0 -9
- data/lib/rest-core/engine/http-client.rb +0 -41
- data/lib/rest-core/error.rb +0 -5
- data/lib/rest-core/event_source.rb +0 -137
- data/lib/rest-core/middleware.rb +0 -151
- data/lib/rest-core/promise.rb +0 -249
- data/lib/rest-core/thread_pool.rb +0 -131
- data/lib/rest-core/timer.rb +0 -58
- data/lib/rest-core/util/payload.rb +0 -173
- data/test/test_builder.rb +0 -40
- data/test/test_client.rb +0 -177
- data/test/test_event_source.rb +0 -159
- data/test/test_future.rb +0 -16
- data/test/test_httpclient.rb +0 -118
- data/test/test_payload.rb +0 -204
- data/test/test_promise.rb +0 -146
- data/test/test_simple.rb +0 -38
- data/test/test_thread_pool.rb +0 -10
| @@ -1,75 +1,80 @@ | |
| 1 1 |  | 
| 2 | 
            -
            require ' | 
| 2 | 
            +
            require 'digest/md5'
         | 
| 3 3 |  | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 4 | 
            +
            require 'rest-core/util/parse_query'
         | 
| 5 | 
            +
            require 'rest-core/util/json'
         | 
| 6 6 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 7 | 
            +
            module RestCore
         | 
| 8 | 
            +
              module ClientOauth1
         | 
| 9 | 
            +
                def authorize_url! opts={}
         | 
| 10 | 
            +
                  self.data = ParseQuery.parse_query(
         | 
| 11 | 
            +
                    post(request_token_path, {}, {},
         | 
| 12 | 
            +
                         {:json_response => false}.merge(opts)))
         | 
| 10 13 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 14 | 
            +
                  authorize_url
         | 
| 15 | 
            +
                end
         | 
| 13 16 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            +
                def authorize_url
         | 
| 18 | 
            +
                  url(authorize_path, :oauth_token => oauth_token)
         | 
| 19 | 
            +
                end
         | 
| 17 20 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            +
                def authorize! opts={}
         | 
| 22 | 
            +
                  self.data = ParseQuery.parse_query(
         | 
| 23 | 
            +
                    post(access_token_path, {}, {},
         | 
| 24 | 
            +
                         {:json_response => false}.merge(opts)))
         | 
| 21 25 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 26 | 
            +
                  data['authorized'] = 'true'
         | 
| 27 | 
            +
                  data
         | 
| 28 | 
            +
                end
         | 
| 25 29 |  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 30 | 
            +
                def authorized?
         | 
| 31 | 
            +
                  !!(oauth_token && oauth_token_secret && data['authorized'])
         | 
| 32 | 
            +
                end
         | 
| 29 33 |  | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 34 | 
            +
                def data_json
         | 
| 35 | 
            +
                  Json.encode(data.merge('sig' => calculate_sig))
         | 
| 36 | 
            +
                end
         | 
| 33 37 |  | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 38 | 
            +
                def data_json= json
         | 
| 39 | 
            +
                  self.data = check_sig_and_return_data(Json.decode(json))
         | 
| 40 | 
            +
                rescue Json.const_get(:ParseError)
         | 
| 41 | 
            +
                  self.data = nil
         | 
| 42 | 
            +
                end
         | 
| 39 43 |  | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 44 | 
            +
                def oauth_token
         | 
| 45 | 
            +
                  data['oauth_token'] if data.kind_of?(Hash)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
                def oauth_token= token
         | 
| 48 | 
            +
                  data['oauth_token'] = token if data.kind_of?(Hash)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                def oauth_token_secret
         | 
| 51 | 
            +
                  data['oauth_token_secret'] if data.kind_of?(Hash)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
                def oauth_token_secret= secret
         | 
| 54 | 
            +
                  data['oauth_token_secret'] = secret if data.kind_of?(Hash)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                def oauth_callback
         | 
| 57 | 
            +
                  data['oauth_callback'] if data.kind_of?(Hash)
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
                def oauth_callback= uri
         | 
| 60 | 
            +
                  data['oauth_callback'] = uri if data.kind_of?(Hash)
         | 
| 61 | 
            +
                end
         | 
| 58 62 |  | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            +
                private
         | 
| 64 | 
            +
                def default_data
         | 
| 65 | 
            +
                  {}
         | 
| 66 | 
            +
                end
         | 
| 63 67 |  | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            +
                def check_sig_and_return_data hash
         | 
| 69 | 
            +
                  hash if consumer_secret && hash.kind_of?(Hash) &&
         | 
| 70 | 
            +
                          calculate_sig(hash) == hash['sig']
         | 
| 71 | 
            +
                end
         | 
| 68 72 |  | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 73 | 
            +
                def calculate_sig hash=data
         | 
| 74 | 
            +
                  base = hash.reject{ |(k, _)| k == 'sig' }.sort.map{ |(k, v)|
         | 
| 75 | 
            +
                    "#{Middleware.escape(k.to_s)}=#{Middleware.escape(v.to_s)}"
         | 
| 76 | 
            +
                  }.join('&')
         | 
| 77 | 
            +
                  Digest::MD5.hexdigest("#{Middleware.escape(consumer_secret)}&#{base}")
         | 
| 78 | 
            +
                end
         | 
| 74 79 | 
             
              end
         | 
| 75 80 | 
             
            end
         | 
    
        data/lib/rest-core/event.rb
    CHANGED
    
    | @@ -1,9 +1,6 @@ | |
| 1 1 |  | 
| 2 2 | 
             
            module RestCore
         | 
| 3 | 
            -
               | 
| 4 | 
            -
                RestCore.const_defined?(:EventStruct)
         | 
| 5 | 
            -
             | 
| 6 | 
            -
              class Event < EventStruct
         | 
| 3 | 
            +
              class Event < Struct.new(:duration, :message)
         | 
| 7 4 | 
             
                def name; self.class.name[/(?<=::)\w+$/]; end
         | 
| 8 5 | 
             
                def to_s
         | 
| 9 6 | 
             
                  if duration
         | 
| @@ -13,11 +10,12 @@ module RestCore | |
| 13 10 | 
             
                  end
         | 
| 14 11 | 
             
                end
         | 
| 15 12 | 
             
              end
         | 
| 16 | 
            -
             | 
| 17 | 
            -
               | 
| 18 | 
            -
               | 
| 19 | 
            -
               | 
| 20 | 
            -
               | 
| 21 | 
            -
               | 
| 22 | 
            -
               | 
| 13 | 
            +
             | 
| 14 | 
            +
              Event::MultiDone    = Class.new(Event)
         | 
| 15 | 
            +
              Event::Requested    = Class.new(Event)
         | 
| 16 | 
            +
              Event::CacheHit     = Class.new(Event)
         | 
| 17 | 
            +
              Event::CacheCleared = Class.new(Event)
         | 
| 18 | 
            +
              Event::Failed       = Class.new(Event)
         | 
| 19 | 
            +
              Event::WithHeader   = Class.new(Event)
         | 
| 20 | 
            +
              Event::Retrying     = Class.new(Event)
         | 
| 23 21 | 
             
            end
         | 
| @@ -1,29 +1,29 @@ | |
| 1 1 |  | 
| 2 | 
            -
             | 
| 2 | 
            +
            module RestCore
         | 
| 3 | 
            +
              class AuthBasic
         | 
| 4 | 
            +
                def self.members; [:username, :password]; end
         | 
| 5 | 
            +
                include Middleware
         | 
| 3 6 |  | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 7 | 
            +
                def call env, &k
         | 
| 8 | 
            +
                  if username(env)
         | 
| 9 | 
            +
                    if password(env)
         | 
| 10 | 
            +
                      app.call(env.merge(REQUEST_HEADERS =>
         | 
| 11 | 
            +
                        auth_basic_header(env).merge(env[REQUEST_HEADERS])), &k)
         | 
| 12 | 
            +
                    else
         | 
| 13 | 
            +
                      app.call(log(env, "AuthBasic: username provided: #{username(env)},"\
         | 
| 14 | 
            +
                                        " but password is missing."), &k)
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  elsif password(env)
         | 
| 17 | 
            +
                    app.call(log(env, "AuthBasic: password provided: #{password(env)}," \
         | 
| 18 | 
            +
                                      " but username is missing."), &k)
         | 
| 13 19 | 
             
                  else
         | 
| 14 | 
            -
                    app.call( | 
| 15 | 
            -
                                      " but password is missing."), &k)
         | 
| 20 | 
            +
                    app.call(env, &k)
         | 
| 16 21 | 
             
                  end
         | 
| 17 | 
            -
                elsif password(env)
         | 
| 18 | 
            -
                  app.call(log(env, "AuthBasic: password provided: #{password(env)}," \
         | 
| 19 | 
            -
                                    " but username is missing."), &k)
         | 
| 20 | 
            -
                else
         | 
| 21 | 
            -
                  app.call(env, &k)
         | 
| 22 22 | 
             
                end
         | 
| 23 | 
            -
              end
         | 
| 24 23 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 24 | 
            +
                def auth_basic_header env
         | 
| 25 | 
            +
                  {'Authorization' =>
         | 
| 26 | 
            +
                     "Basic #{["#{username(env)}:#{password(env)}"].pack('m0')}"}
         | 
| 27 | 
            +
                end
         | 
| 28 28 | 
             
              end
         | 
| 29 29 | 
             
            end
         | 
| @@ -1,13 +1,13 @@ | |
| 1 1 |  | 
| 2 | 
            -
            module RestCore; end
         | 
| 3 | 
            -
             | 
| 4 2 | 
             
            # the simplest middleware
         | 
| 5 | 
            -
             | 
| 6 | 
            -
               | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 3 | 
            +
            module RestCore
         | 
| 4 | 
            +
              class Bypass
         | 
| 5 | 
            +
                def initialize app
         | 
| 6 | 
            +
                  @app = app
         | 
| 7 | 
            +
                end
         | 
| 9 8 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 9 | 
            +
                def call env, &k
         | 
| 10 | 
            +
                  @app.call(env, &k)
         | 
| 11 | 
            +
                end
         | 
| 12 12 | 
             
              end
         | 
| 13 13 | 
             
            end
         | 
| @@ -1,117 +1,121 @@ | |
| 1 1 |  | 
| 2 | 
            -
            require 'rest-core/event'
         | 
| 3 | 
            -
            require 'rest-core/middleware'
         | 
| 4 | 
            -
             | 
| 5 2 | 
             
            require 'digest/md5'
         | 
| 3 | 
            +
            require 'rest-core/event'
         | 
| 6 4 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
               | 
| 9 | 
            -
             | 
| 5 | 
            +
            module RestCore
         | 
| 6 | 
            +
              class Cache
         | 
| 7 | 
            +
                def self.members; [:cache, :expires_in]; end
         | 
| 8 | 
            +
                include Middleware
         | 
| 10 9 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 10 | 
            +
                def initialize app, cache, expires_in, &block
         | 
| 11 | 
            +
                  super(&block)
         | 
| 12 | 
            +
                  @app, @cache, @expires_in = app, cache, expires_in
         | 
| 13 | 
            +
                end
         | 
| 15 14 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 15 | 
            +
                def call env, &k
         | 
| 16 | 
            +
                  e = if env['cache.update']
         | 
| 17 | 
            +
                        cache_clear(env)
         | 
| 18 | 
            +
                      else
         | 
| 19 | 
            +
                        env
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  cache_get(e){ |cached|
         | 
| 23 | 
            +
                    e[TIMER].cancel if e[TIMER]
         | 
| 24 | 
            +
                    k.call(cached)
         | 
| 25 | 
            +
                  } || app_call(e, &k)
         | 
| 26 | 
            +
                end
         | 
| 28 27 |  | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 28 | 
            +
                def app_call env
         | 
| 29 | 
            +
                  app.call(env) do |res|
         | 
| 30 | 
            +
                    yield(if (res[FAIL] || []).empty?
         | 
| 31 | 
            +
                            cache_for(res)
         | 
| 32 | 
            +
                          else
         | 
| 33 | 
            +
                            res
         | 
| 34 | 
            +
                          end)
         | 
| 35 | 
            +
                  end
         | 
| 36 36 | 
             
                end
         | 
| 37 | 
            -
              end
         | 
| 38 37 |  | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 38 | 
            +
                def cache_key env
         | 
| 39 | 
            +
                  "rest-core:cache:#{Digest::MD5.hexdigest(env['cache.key'] ||
         | 
| 40 | 
            +
                                                           cache_key_raw(env))}"
         | 
| 41 | 
            +
                end
         | 
| 43 42 |  | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 43 | 
            +
                def cache_get env, &k
         | 
| 44 | 
            +
                  return unless cache(env)
         | 
| 45 | 
            +
                  return unless cache_for?(env)
         | 
| 47 46 |  | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 47 | 
            +
                  uri = request_uri(env)
         | 
| 48 | 
            +
                  start_time = Time.now
         | 
| 49 | 
            +
                  return unless data = cache(env)[cache_key(env)]
         | 
| 50 | 
            +
                  res = log(env.merge(REQUEST_URI => uri),
         | 
| 51 | 
            +
                            Event::CacheHit.new(Time.now - start_time, uri))
         | 
| 52 | 
            +
                  data_extract(data, res, k)
         | 
| 53 | 
            +
                end
         | 
| 55 54 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 55 | 
            +
                private
         | 
| 56 | 
            +
                def cache_key_raw env
         | 
| 57 | 
            +
                  "#{env[REQUEST_METHOD]}:#{request_uri(env)}:#{header_cache_key(env)}"
         | 
| 58 | 
            +
                end
         | 
| 60 59 |  | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 60 | 
            +
                def cache_clear env
         | 
| 61 | 
            +
                  return env unless cache(env)
         | 
| 62 | 
            +
                  return env unless cache_for?(env)
         | 
| 64 63 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 64 | 
            +
                  cache_store(env, :[]=, nil)
         | 
| 65 | 
            +
                end
         | 
| 67 66 |  | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 67 | 
            +
                def cache_for res
         | 
| 68 | 
            +
                  return res unless cache(res)
         | 
| 69 | 
            +
                  return res unless cache_for?(res)
         | 
| 71 70 |  | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 71 | 
            +
                  if expires_in(res).kind_of?(Fixnum) &&
         | 
| 72 | 
            +
                     cache(res).respond_to?(:store)   &&
         | 
| 73 | 
            +
                     cache(res).method(:store).arity == -3
         | 
| 75 74 |  | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 75 | 
            +
                    cache_store(res, :store, data_construct(res),
         | 
| 76 | 
            +
                                               :expires_in => expires_in(res))
         | 
| 77 | 
            +
                  else
         | 
| 78 | 
            +
                    cache_store(res, :[]=  , data_construct(res))
         | 
| 79 | 
            +
                  end
         | 
| 80 80 | 
             
                end
         | 
| 81 | 
            -
              end
         | 
| 82 81 |  | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 82 | 
            +
                def cache_store res, msg, value, *args
         | 
| 83 | 
            +
                  start_time = Time.now
         | 
| 84 | 
            +
                  cache(res).send(msg, cache_key(res), value, *args)
         | 
| 86 85 |  | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 86 | 
            +
                  if value
         | 
| 87 | 
            +
                    res
         | 
| 88 | 
            +
                  else
         | 
| 89 | 
            +
                    log(res,
         | 
| 90 | 
            +
                      Event::CacheCleared.new(Time.now - start_time, request_uri(res)))
         | 
| 91 | 
            +
                  end
         | 
| 92 92 | 
             
                end
         | 
| 93 | 
            -
              end
         | 
| 94 93 |  | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 94 | 
            +
                def data_construct res
         | 
| 95 | 
            +
                  "#{ res[RESPONSE_STATUS]}\n" \
         | 
| 96 | 
            +
                  "#{(res[RESPONSE_HEADERS]||{}).map{|k,v|"#{k}: #{v}\n"}.join}\n\n" \
         | 
| 97 | 
            +
                  "#{ res[RESPONSE_BODY]}"
         | 
| 98 | 
            +
                end
         | 
| 100 99 |  | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 100 | 
            +
                def data_extract data, res, k
         | 
| 101 | 
            +
                  _, status, headers, body =
         | 
| 102 | 
            +
                    data.match(/\A(\d*)\n((?:[^\n]+\n)*)\n\n(.*)\Z/m).to_a
         | 
| 104 103 |  | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 104 | 
            +
                  result = res.merge(RESPONSE_STATUS => status.to_i,
         | 
| 105 | 
            +
                                     RESPONSE_HEADERS =>
         | 
| 106 | 
            +
                                       Hash[(headers||'').scan(/([^:]+): ([^\n]+)\n/)],
         | 
| 107 | 
            +
                                     RESPONSE_BODY => body)
         | 
| 108 108 |  | 
| 109 | 
            -
             | 
| 110 | 
            -
                 | 
| 111 | 
            -
                  !env[DRY] && !env[HIJACK]
         | 
| 112 | 
            -
              end
         | 
| 109 | 
            +
                  result.merge(Promise.claim(result, &k).future_response)
         | 
| 110 | 
            +
                end
         | 
| 113 111 |  | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 112 | 
            +
                def cache_for? env
         | 
| 113 | 
            +
                  [:get, :head, :otpions].include?(env[REQUEST_METHOD]) &&
         | 
| 114 | 
            +
                    !env[DRY] && !env[HIJACK]
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                def header_cache_key env
         | 
| 118 | 
            +
                  env[REQUEST_HEADERS].sort.map{|(k,v)|"#{k}=#{v}"}.join('&')
         | 
| 119 | 
            +
                end
         | 
| 116 120 | 
             
              end
         | 
| 117 121 | 
             
            end
         |