avdi-faraday 0.8.1
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.
- data/Gemfile +27 -0
- data/LICENSE.md +20 -0
- data/README.md +250 -0
- data/Rakefile +87 -0
- data/config.ru +6 -0
- data/faraday.gemspec +86 -0
- data/lib/faraday.rb +276 -0
- data/lib/faraday/adapter.rb +71 -0
- data/lib/faraday/adapter/em_http.rb +217 -0
- data/lib/faraday/adapter/em_synchrony.rb +89 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/excon.rb +59 -0
- data/lib/faraday/adapter/httpclient.rb +92 -0
- data/lib/faraday/adapter/net_http.rb +116 -0
- data/lib/faraday/adapter/net_http_persistent.rb +37 -0
- data/lib/faraday/adapter/patron.rb +65 -0
- data/lib/faraday/adapter/rack.rb +57 -0
- data/lib/faraday/adapter/test.rb +162 -0
- data/lib/faraday/adapter/typhoeus.rb +107 -0
- data/lib/faraday/builder.rb +184 -0
- data/lib/faraday/connection.rb +468 -0
- data/lib/faraday/error.rb +40 -0
- data/lib/faraday/middleware.rb +41 -0
- data/lib/faraday/request.rb +101 -0
- data/lib/faraday/request/authorization.rb +40 -0
- data/lib/faraday/request/basic_authentication.rb +13 -0
- data/lib/faraday/request/multipart.rb +62 -0
- data/lib/faraday/request/retry.rb +67 -0
- data/lib/faraday/request/token_authentication.rb +15 -0
- data/lib/faraday/request/url_encoded.rb +35 -0
- data/lib/faraday/response.rb +99 -0
- data/lib/faraday/response/logger.rb +34 -0
- data/lib/faraday/response/raise_error.rb +16 -0
- data/lib/faraday/upload_io.rb +23 -0
- data/lib/faraday/utils.rb +274 -0
- data/script/test +91 -0
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +19 -0
- data/test/adapters/em_synchrony_test.rb +20 -0
- data/test/adapters/excon_test.rb +15 -0
- data/test/adapters/httpclient_test.rb +16 -0
- data/test/adapters/integration.rb +193 -0
- data/test/adapters/logger_test.rb +37 -0
- data/test/adapters/net_http_persistent_test.rb +11 -0
- data/test/adapters/net_http_test.rb +49 -0
- data/test/adapters/patron_test.rb +17 -0
- data/test/adapters/rack_test.rb +26 -0
- data/test/adapters/test_middleware_test.rb +70 -0
- data/test/adapters/typhoeus_test.rb +20 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/connection_test.rb +375 -0
- data/test/env_test.rb +183 -0
- data/test/helper.rb +75 -0
- data/test/live_server.rb +57 -0
- data/test/middleware/retry_test.rb +62 -0
- data/test/middleware_stack_test.rb +203 -0
- data/test/middleware_test.rb +12 -0
- data/test/request_middleware_test.rb +108 -0
- data/test/response_middleware_test.rb +74 -0
- metadata +182 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module Faraday
|
2
|
+
module Error
|
3
|
+
class ClientError < StandardError
|
4
|
+
attr_reader :response
|
5
|
+
|
6
|
+
def initialize(ex, response = nil)
|
7
|
+
@wrapped_exception = nil
|
8
|
+
@response = response
|
9
|
+
|
10
|
+
if ex.respond_to?(:backtrace)
|
11
|
+
super(ex.message)
|
12
|
+
@wrapped_exception = ex
|
13
|
+
elsif ex.respond_to?(:each_key)
|
14
|
+
super("the server responded with status #{ex[:status]}")
|
15
|
+
@response = ex
|
16
|
+
else
|
17
|
+
super(ex.to_s)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def backtrace
|
22
|
+
if @wrapped_exception
|
23
|
+
@wrapped_exception.backtrace
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
%(#<#{self.class}>)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ConnectionFailed < ClientError; end
|
35
|
+
class ResourceNotFound < ClientError; end
|
36
|
+
class ParsingError < ClientError; end
|
37
|
+
class TimeoutError < ClientError; end
|
38
|
+
class MissingDependency < StandardError; end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Middleware
|
3
|
+
extend MiddlewareRegistry
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :load_error
|
7
|
+
private :load_error=
|
8
|
+
end
|
9
|
+
|
10
|
+
self.load_error = nil
|
11
|
+
|
12
|
+
# Executes a block which should try to require and reference dependent libraries
|
13
|
+
def self.dependency(lib = nil)
|
14
|
+
lib ? require(lib) : yield
|
15
|
+
rescue LoadError, NameError => error
|
16
|
+
self.load_error = error
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.new(*)
|
20
|
+
raise "missing dependency for #{self}: #{load_error.message}" unless loaded?
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.loaded?
|
25
|
+
load_error.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.inherited(subclass)
|
29
|
+
super
|
30
|
+
subclass.send(:load_error=, self.load_error)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.adapter?
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(app = nil)
|
38
|
+
@app = app
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Used to setup urls, params, headers, and the request body in a sane manner.
|
3
|
+
#
|
4
|
+
# @connection.post do |req|
|
5
|
+
# req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
|
6
|
+
# req.headers['b'] = '2' # Header
|
7
|
+
# req.params['c'] = '3' # GET Param
|
8
|
+
# req['b'] = '2' # also Header
|
9
|
+
# req.body = 'abc'
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
|
13
|
+
extend AutoloadHelper
|
14
|
+
extend MiddlewareRegistry
|
15
|
+
|
16
|
+
autoload_all 'faraday/request',
|
17
|
+
:UrlEncoded => 'url_encoded',
|
18
|
+
:Multipart => 'multipart',
|
19
|
+
:Retry => 'retry',
|
20
|
+
:Timeout => 'timeout',
|
21
|
+
:Authorization => 'authorization',
|
22
|
+
:BasicAuthentication => 'basic_authentication',
|
23
|
+
:TokenAuthentication => 'token_authentication'
|
24
|
+
|
25
|
+
register_middleware \
|
26
|
+
:url_encoded => :UrlEncoded,
|
27
|
+
:multipart => :Multipart,
|
28
|
+
:retry => :Retry,
|
29
|
+
:authorization => :Authorization,
|
30
|
+
:basic_auth => :BasicAuthentication,
|
31
|
+
:token_auth => :TokenAuthentication
|
32
|
+
|
33
|
+
def self.create(request_method)
|
34
|
+
new(request_method).tap do |request|
|
35
|
+
yield request if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Public: Replace params, preserving the existing hash type
|
40
|
+
def params=(hash)
|
41
|
+
if params then params.replace hash
|
42
|
+
else super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: Replace request headers, preserving the existing hash type
|
47
|
+
def headers=(hash)
|
48
|
+
if headers then headers.replace hash
|
49
|
+
else super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def url(path, params = nil)
|
54
|
+
if path.respond_to? :query
|
55
|
+
if query = path.query
|
56
|
+
path = path.dup
|
57
|
+
path.query = nil
|
58
|
+
end
|
59
|
+
else
|
60
|
+
path, query = path.split('?', 2)
|
61
|
+
end
|
62
|
+
self.path = path
|
63
|
+
self.params.merge_query query
|
64
|
+
self.params.update(params) if params
|
65
|
+
end
|
66
|
+
|
67
|
+
def [](key)
|
68
|
+
headers[key]
|
69
|
+
end
|
70
|
+
|
71
|
+
def []=(key, value)
|
72
|
+
headers[key] = value
|
73
|
+
end
|
74
|
+
|
75
|
+
# ENV Keys
|
76
|
+
# :method - a symbolized request method (:get, :post)
|
77
|
+
# :body - the request body that will eventually be converted to a string.
|
78
|
+
# :url - URI instance for the current request.
|
79
|
+
# :status - HTTP response status code
|
80
|
+
# :request_headers - hash of HTTP Headers to be sent to the server
|
81
|
+
# :response_headers - Hash of HTTP headers from the server
|
82
|
+
# :parallel_manager - sent if the connection is in parallel mode
|
83
|
+
# :request - Hash of options for configuring the request.
|
84
|
+
# :timeout - open/read timeout Integer in seconds
|
85
|
+
# :open_timeout - read timeout Integer in seconds
|
86
|
+
# :proxy - Hash of proxy options
|
87
|
+
# :uri - Proxy Server URI
|
88
|
+
# :user - Proxy server username
|
89
|
+
# :password - Proxy server password
|
90
|
+
# :ssl - Hash of options for configuring SSL requests.
|
91
|
+
def to_env(connection)
|
92
|
+
{ :method => method,
|
93
|
+
:body => body,
|
94
|
+
:url => connection.build_exclusive_url(path, params),
|
95
|
+
:request_headers => headers,
|
96
|
+
:parallel_manager => connection.parallel_manager,
|
97
|
+
:request => options,
|
98
|
+
:ssl => connection.ssl}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::Authorization < Faraday::Middleware
|
3
|
+
KEY = "Authorization".freeze
|
4
|
+
|
5
|
+
# Public
|
6
|
+
def self.header(type, token)
|
7
|
+
case token
|
8
|
+
when String, Symbol then "#{type} #{token}"
|
9
|
+
when Hash then build_hash(type.to_s, token)
|
10
|
+
else
|
11
|
+
raise ArgumentError, "Can't build an Authorization #{type} header from #{token.inspect}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Internal
|
16
|
+
def self.build_hash(type, hash)
|
17
|
+
offset = KEY.size + type.size + 3
|
18
|
+
comma = ",\n#{' ' * offset}"
|
19
|
+
values = []
|
20
|
+
hash.each do |key, value|
|
21
|
+
values << "#{key}=#{value.to_s.inspect}"
|
22
|
+
end
|
23
|
+
"#{type} #{values * comma}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(app, type, token)
|
27
|
+
@header_value = self.class.header(type, token)
|
28
|
+
super(app)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public
|
32
|
+
def call(env)
|
33
|
+
unless env[:request_headers][KEY]
|
34
|
+
env[:request_headers][KEY] = @header_value
|
35
|
+
end
|
36
|
+
@app.call(env)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
class Request::BasicAuthentication < Request::Authorization
|
5
|
+
# Public
|
6
|
+
def self.header(login, pass)
|
7
|
+
value = Base64.encode64([login, pass].join(':'))
|
8
|
+
value.gsub!("\n", '')
|
9
|
+
super(:Basic, value)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::Multipart < Request::UrlEncoded
|
3
|
+
self.mime_type = 'multipart/form-data'.freeze
|
4
|
+
DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze
|
5
|
+
|
6
|
+
def call(env)
|
7
|
+
match_content_type(env) do |params|
|
8
|
+
env[:request] ||= {}
|
9
|
+
env[:request][:boundary] ||= DEFAULT_BOUNDARY
|
10
|
+
env[:request_headers][CONTENT_TYPE] += ";boundary=#{env[:request][:boundary]}"
|
11
|
+
env[:body] = create_multipart(env, params)
|
12
|
+
end
|
13
|
+
@app.call env
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_request?(env)
|
17
|
+
type = request_type(env)
|
18
|
+
env[:body].respond_to?(:each_key) and !env[:body].empty? and (
|
19
|
+
(type.empty? and has_multipart?(env[:body])) or
|
20
|
+
type == self.class.mime_type
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_multipart?(obj)
|
25
|
+
# string is an enum in 1.8, returning list of itself
|
26
|
+
if obj.respond_to?(:each) && !obj.is_a?(String)
|
27
|
+
(obj.respond_to?(:values) ? obj.values : obj).each do |val|
|
28
|
+
return true if (val.respond_to?(:content_type) || has_multipart?(val))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_multipart(env, params)
|
35
|
+
boundary = env[:request][:boundary]
|
36
|
+
parts = process_params(params) do |key, value|
|
37
|
+
Faraday::Parts::Part.new(boundary, key, value)
|
38
|
+
end
|
39
|
+
parts << Faraday::Parts::EpiloguePart.new(boundary)
|
40
|
+
|
41
|
+
body = Faraday::CompositeReadIO.new(parts)
|
42
|
+
env[:request_headers]['Content-Length'] = body.length.to_s
|
43
|
+
return body
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_params(params, prefix = nil, pieces = nil, &block)
|
47
|
+
params.inject(pieces || []) do |all, (key, value)|
|
48
|
+
key = "#{prefix}[#{key}]" if prefix
|
49
|
+
|
50
|
+
case value
|
51
|
+
when Array
|
52
|
+
values = value.inject([]) { |a,v| a << [nil, v] }
|
53
|
+
process_params(values, key, all, &block)
|
54
|
+
when Hash
|
55
|
+
process_params(value, key, all, &block)
|
56
|
+
else
|
57
|
+
all << block.call(key, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Catches exceptions and retries each request a limited number of times.
|
3
|
+
#
|
4
|
+
# By default, it retries 2 times and handles only timeout exceptions. It can
|
5
|
+
# be configured with an arbitrary number of retries, a list of exceptions to
|
6
|
+
# handle an a retry interval.
|
7
|
+
#
|
8
|
+
# Examples
|
9
|
+
#
|
10
|
+
# Faraday.new do |conn|
|
11
|
+
# conn.request :retry, max: 2, interval: 0.05,
|
12
|
+
# exceptions: [CustomException, 'Timeout::Error']
|
13
|
+
# conn.adapter ...
|
14
|
+
# end
|
15
|
+
class Request::Retry < Faraday::Middleware
|
16
|
+
# Public: Initialize middleware
|
17
|
+
#
|
18
|
+
# Options:
|
19
|
+
# max - Maximum number of retries (default: 2).
|
20
|
+
# interval - Pause in seconds between retries (default: 0).
|
21
|
+
# exceptions - The list of exceptions to handle. Exceptions can be
|
22
|
+
# given as Class, Module, or String. (default:
|
23
|
+
# [Errno::ETIMEDOUT, Timeout::Error, Error::TimeoutError])
|
24
|
+
def initialize(app, options = {})
|
25
|
+
super(app)
|
26
|
+
@retries, options = options, {} if options.is_a? Integer
|
27
|
+
@retries ||= options.fetch(:max, 2).to_i
|
28
|
+
@sleep = options.fetch(:interval, 0).to_f
|
29
|
+
to_handle = options.fetch(:exceptions) {
|
30
|
+
[Errno::ETIMEDOUT, 'Timeout::Error', Error::TimeoutError]
|
31
|
+
}
|
32
|
+
@errmatch = build_exception_matcher Array(to_handle)
|
33
|
+
end
|
34
|
+
|
35
|
+
def call(env)
|
36
|
+
retries = @retries
|
37
|
+
begin
|
38
|
+
@app.call(env)
|
39
|
+
rescue @errmatch
|
40
|
+
if retries > 0
|
41
|
+
retries -= 1
|
42
|
+
sleep @sleep if @sleep > 0
|
43
|
+
retry
|
44
|
+
end
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Private: construct an exception matcher object.
|
50
|
+
#
|
51
|
+
# An exception matcher for the rescue clause can usually be any object that
|
52
|
+
# responds to `===`, but for Ruby 1.8 it has to be a Class or Module.
|
53
|
+
def build_exception_matcher(exceptions)
|
54
|
+
matcher = Module.new
|
55
|
+
(class << matcher; self; end).class_eval do
|
56
|
+
define_method(:===) do |error|
|
57
|
+
exceptions.any? do |ex|
|
58
|
+
if ex.is_a? Module then error.is_a? ex
|
59
|
+
else error.class.to_s == ex.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
matcher
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::TokenAuthentication < Request::Authorization
|
3
|
+
# Public
|
4
|
+
def self.header(token, options = nil)
|
5
|
+
options ||= {}
|
6
|
+
options[:token] = token
|
7
|
+
super :Token, options
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(app, token, options = nil)
|
11
|
+
super(app, token, options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::UrlEncoded < Faraday::Middleware
|
3
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :mime_type
|
7
|
+
end
|
8
|
+
self.mime_type = 'application/x-www-form-urlencoded'.freeze
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
match_content_type(env) do |data|
|
12
|
+
env[:body] = Faraday::Utils.build_nested_query data
|
13
|
+
end
|
14
|
+
@app.call env
|
15
|
+
end
|
16
|
+
|
17
|
+
def match_content_type(env)
|
18
|
+
if process_request?(env)
|
19
|
+
env[:request_headers][CONTENT_TYPE] ||= self.class.mime_type
|
20
|
+
yield env[:body] unless env[:body].respond_to?(:to_str)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_request?(env)
|
25
|
+
type = request_type(env)
|
26
|
+
env[:body] and (type.empty? or type == self.class.mime_type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def request_type(env)
|
30
|
+
type = env[:request_headers][CONTENT_TYPE].to_s
|
31
|
+
type = type.split(';', 2).first if type.index(';')
|
32
|
+
type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
class Response
|
5
|
+
# Used for simple response middleware.
|
6
|
+
class Middleware < Faraday::Middleware
|
7
|
+
def call(env)
|
8
|
+
@app.call(env).on_complete do |environment|
|
9
|
+
on_complete(environment)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Override this to modify the environment after the response has finished.
|
14
|
+
# Calls the `parse` method if defined
|
15
|
+
def on_complete(env)
|
16
|
+
if respond_to? :parse
|
17
|
+
env[:body] = parse(env[:body]) unless [204,304].index env[:status]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
extend Forwardable
|
23
|
+
extend AutoloadHelper
|
24
|
+
extend MiddlewareRegistry
|
25
|
+
|
26
|
+
autoload_all 'faraday/response',
|
27
|
+
:RaiseError => 'raise_error',
|
28
|
+
:Logger => 'logger'
|
29
|
+
|
30
|
+
register_middleware \
|
31
|
+
:raise_error => :RaiseError,
|
32
|
+
:logger => :Logger
|
33
|
+
|
34
|
+
def initialize(env = nil)
|
35
|
+
@env = env
|
36
|
+
@on_complete_callbacks = []
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :env
|
40
|
+
alias_method :to_hash, :env
|
41
|
+
|
42
|
+
def status
|
43
|
+
finished? ? env[:status] : nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def headers
|
47
|
+
finished? ? env[:response_headers] : {}
|
48
|
+
end
|
49
|
+
def_delegator :headers, :[]
|
50
|
+
|
51
|
+
def body
|
52
|
+
finished? ? env[:body] : nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def finished?
|
56
|
+
!!env
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_complete
|
60
|
+
if not finished?
|
61
|
+
@on_complete_callbacks << Proc.new
|
62
|
+
else
|
63
|
+
yield env
|
64
|
+
end
|
65
|
+
return self
|
66
|
+
end
|
67
|
+
|
68
|
+
def finish(env)
|
69
|
+
raise "response already finished" if finished?
|
70
|
+
@env = env
|
71
|
+
@on_complete_callbacks.each { |callback| callback.call(env) }
|
72
|
+
return self
|
73
|
+
end
|
74
|
+
|
75
|
+
def success?
|
76
|
+
(200..299).include?(status)
|
77
|
+
end
|
78
|
+
|
79
|
+
# because @on_complete_callbacks cannot be marshalled
|
80
|
+
def marshal_dump
|
81
|
+
!finished? ? nil : {
|
82
|
+
:status => @env[:status], :body => @env[:body],
|
83
|
+
:response_headers => @env[:response_headers]
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def marshal_load(env)
|
88
|
+
@env = env
|
89
|
+
end
|
90
|
+
|
91
|
+
# Expand the env with more properties, without overriding existing ones.
|
92
|
+
# Useful for applying request params after restoring a marshalled Response.
|
93
|
+
def apply_request(request_env)
|
94
|
+
raise "response didn't finish yet" unless finished?
|
95
|
+
@env = request_env.merge @env
|
96
|
+
return self
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|