agiley-faraday_middleware 0.8.3.2 → 0.9.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.
- data/.gitignore +2 -2
- data/.rspec +2 -1
- data/.travis.yml +6 -3
- data/Gemfile +5 -1
- data/Rakefile +16 -4
- data/faraday_middleware.gemspec +1 -1
- data/lib/faraday_middleware.rb +17 -13
- data/lib/faraday_middleware/instrumentation.rb +2 -2
- data/lib/faraday_middleware/rack_compatible.rb +14 -9
- data/lib/faraday_middleware/request/encode_json.rb +4 -2
- data/lib/faraday_middleware/request/method_override.rb +51 -0
- data/lib/faraday_middleware/request/oauth.rb +28 -5
- data/lib/faraday_middleware/response/caching.rb +7 -2
- data/lib/faraday_middleware/response/chunked.rb +29 -0
- data/lib/faraday_middleware/response/follow_redirects.rb +100 -11
- data/lib/faraday_middleware/response/mashify.rb +11 -2
- data/lib/faraday_middleware/response/parse_dates.rb +39 -0
- data/lib/faraday_middleware/response/parse_json.rb +17 -5
- data/lib/faraday_middleware/response/parse_marshal.rb +2 -2
- data/lib/faraday_middleware/response/parse_xml.rb +3 -2
- data/lib/faraday_middleware/response/parse_yaml.rb +3 -1
- data/lib/faraday_middleware/response/rashify.rb +2 -0
- data/lib/faraday_middleware/response_middleware.rb +2 -2
- data/lib/faraday_middleware/version.rb +1 -1
- data/spec/caching_test.rb +37 -4
- data/spec/chunked_spec.rb +78 -0
- data/spec/encode_json_spec.rb +13 -13
- data/spec/follow_redirects_spec.rb +203 -18
- data/spec/helper.rb +15 -1
- data/spec/mashify_spec.rb +46 -26
- data/spec/method_override_spec.rb +92 -0
- data/spec/oauth2_spec.rb +18 -18
- data/spec/oauth_spec.rb +67 -17
- data/spec/parse_dates_spec.rb +39 -0
- data/spec/parse_json_spec.rb +37 -19
- data/spec/parse_marshal_spec.rb +3 -3
- data/spec/parse_xml_spec.rb +13 -13
- data/spec/parse_yaml_spec.rb +10 -10
- data/spec/rashify_spec.rb +26 -23
- metadata +78 -21
@@ -1,7 +1,11 @@
|
|
1
1
|
require 'faraday'
|
2
2
|
|
3
3
|
module FaradayMiddleware
|
4
|
+
# Public: Converts parsed response bodies to a Hashie::Mash if they were of
|
5
|
+
# Hash or Array type.
|
4
6
|
class Mashify < Faraday::Response::Middleware
|
7
|
+
attr_accessor :mash_class
|
8
|
+
|
5
9
|
class << self
|
6
10
|
attr_accessor :mash_class
|
7
11
|
end
|
@@ -11,12 +15,17 @@ module FaradayMiddleware
|
|
11
15
|
self.mash_class = ::Hashie::Mash
|
12
16
|
end
|
13
17
|
|
18
|
+
def initialize(app = nil, options = {})
|
19
|
+
super(app)
|
20
|
+
self.mash_class = options[:mash_class] || self.class.mash_class
|
21
|
+
end
|
22
|
+
|
14
23
|
def parse(body)
|
15
24
|
case body
|
16
25
|
when Hash
|
17
|
-
|
26
|
+
mash_class.new(body)
|
18
27
|
when Array
|
19
|
-
body.map { |item|
|
28
|
+
body.map { |item| parse(item) }
|
20
29
|
else
|
21
30
|
body
|
22
31
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "time"
|
2
|
+
require "faraday"
|
3
|
+
|
4
|
+
module FaradayMiddleware
|
5
|
+
# Parse dates from response body
|
6
|
+
class ParseDates < ::Faraday::Response::Middleware
|
7
|
+
ISO_DATE_FORMAT = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z\Z/m
|
8
|
+
|
9
|
+
def initialize(app, options = {})
|
10
|
+
@regexp = options[:match] || ISO_DATE_FORMAT
|
11
|
+
super(app)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
response = @app.call(env)
|
16
|
+
parse_dates! response.env[:body]
|
17
|
+
response
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parse_dates!(value)
|
23
|
+
case value
|
24
|
+
when Hash
|
25
|
+
value.each do |key, element|
|
26
|
+
value[key] = parse_dates!(element)
|
27
|
+
end
|
28
|
+
when Array
|
29
|
+
value.each_with_index do |element, index|
|
30
|
+
value[index] = parse_dates!(element)
|
31
|
+
end
|
32
|
+
when @regexp
|
33
|
+
Time.parse(value)
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -3,11 +3,13 @@ require 'faraday_middleware/response_middleware'
|
|
3
3
|
module FaradayMiddleware
|
4
4
|
# Public: Parse response bodies as JSON.
|
5
5
|
class ParseJson < ResponseMiddleware
|
6
|
-
dependency
|
6
|
+
dependency do
|
7
|
+
require 'json' unless defined?(::JSON)
|
8
|
+
end
|
7
9
|
|
8
|
-
define_parser
|
9
|
-
JSON.parse body unless body.empty?
|
10
|
-
|
10
|
+
define_parser do |body|
|
11
|
+
::JSON.parse body unless body.strip.empty?
|
12
|
+
end
|
11
13
|
|
12
14
|
# Public: Override the content-type of the response with "application/json"
|
13
15
|
# if the response body looks like it might be JSON, i.e. starts with an
|
@@ -26,9 +28,19 @@ module FaradayMiddleware
|
|
26
28
|
end
|
27
29
|
|
28
30
|
BRACKETS = %w- [ { -
|
31
|
+
WHITESPACE = [ " ", "\n", "\r", "\t" ]
|
29
32
|
|
30
33
|
def parse_response?(env)
|
31
|
-
super and BRACKETS.include? env[:body]
|
34
|
+
super and BRACKETS.include? first_char(env[:body])
|
35
|
+
end
|
36
|
+
|
37
|
+
def first_char(body)
|
38
|
+
idx = -1
|
39
|
+
begin
|
40
|
+
char = body[idx += 1]
|
41
|
+
char = char.chr if char
|
42
|
+
end while char and WHITESPACE.include? char
|
43
|
+
char
|
32
44
|
end
|
33
45
|
end
|
34
46
|
end
|
@@ -3,9 +3,9 @@ require 'faraday_middleware/response_middleware'
|
|
3
3
|
module FaradayMiddleware
|
4
4
|
# Public: Restore marshalled Ruby objects in response bodies.
|
5
5
|
class ParseMarshal < ResponseMiddleware
|
6
|
-
define_parser
|
6
|
+
define_parser do |body|
|
7
7
|
::Marshal.load body unless body.empty?
|
8
|
-
|
8
|
+
end
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'faraday_middleware/response_middleware'
|
2
2
|
|
3
3
|
module FaradayMiddleware
|
4
|
+
# Public: parses response bodies with MultiXml.
|
4
5
|
class ParseXml < ResponseMiddleware
|
5
6
|
dependency 'multi_xml'
|
6
7
|
|
7
|
-
define_parser
|
8
|
+
define_parser do |body|
|
8
9
|
::MultiXml.parse(body)
|
9
|
-
|
10
|
+
end
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
@@ -26,8 +26,8 @@ module FaradayMiddleware
|
|
26
26
|
@content_types = Array(options[:content_type])
|
27
27
|
end
|
28
28
|
|
29
|
-
def call(
|
30
|
-
@app.call(
|
29
|
+
def call(environment)
|
30
|
+
@app.call(environment).on_complete do |env|
|
31
31
|
if process_response_type?(response_type(env)) and parse_response?(env)
|
32
32
|
process_response(env)
|
33
33
|
end
|
data/spec/caching_test.rb
CHANGED
@@ -2,6 +2,7 @@ require 'test/unit'
|
|
2
2
|
require 'forwardable'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'rack/cache'
|
5
|
+
require 'faraday'
|
5
6
|
require 'faraday_middleware/response/caching'
|
6
7
|
require 'faraday_middleware/rack_compatible'
|
7
8
|
|
@@ -22,6 +23,16 @@ class CachingTest < Test::Unit::TestCase
|
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
26
|
+
class Lint < Struct.new(:app)
|
27
|
+
def call(env)
|
28
|
+
app.call(env).on_complete do
|
29
|
+
raise "no headers" unless env[:response_headers].is_a? Hash
|
30
|
+
raise "no response" unless env[:response].is_a? Faraday::Response
|
31
|
+
raise "env not identical" unless env[:response].env.object_id == env.object_id
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
25
36
|
def setup
|
26
37
|
@cache = TestCache.new
|
27
38
|
|
@@ -31,6 +42,7 @@ class CachingTest < Test::Unit::TestCase
|
|
31
42
|
}
|
32
43
|
|
33
44
|
@conn = Faraday.new do |b|
|
45
|
+
b.use Lint
|
34
46
|
b.use FaradayMiddleware::Caching, @cache
|
35
47
|
b.adapter :test do |stub|
|
36
48
|
stub.get('/', &response)
|
@@ -55,7 +67,7 @@ class CachingTest < Test::Unit::TestCase
|
|
55
67
|
get('/') # make cache
|
56
68
|
response = get('/')
|
57
69
|
assert_equal :get, response.env[:method]
|
58
|
-
assert_equal '/', response.env[:url].
|
70
|
+
assert_equal '/', response.env[:url].request_uri
|
59
71
|
end
|
60
72
|
|
61
73
|
def test_cache_query_params
|
@@ -76,10 +88,22 @@ end
|
|
76
88
|
class HttpCachingTest < Test::Unit::TestCase
|
77
89
|
include FileUtils
|
78
90
|
|
79
|
-
CACHE_DIR = File.expand_path('
|
91
|
+
CACHE_DIR = File.expand_path('../../tmp/cache', __FILE__)
|
92
|
+
|
93
|
+
# middleware to check whether "rack.errors" is free of error reports
|
94
|
+
class RackErrorsComplainer < Struct.new(:app)
|
95
|
+
def call(env)
|
96
|
+
response = app.call(env)
|
97
|
+
error_stream = env['rack.errors'].string
|
98
|
+
raise %(unexpected error in 'rack.errors') if error_stream.include? 'error'
|
99
|
+
response
|
100
|
+
end
|
101
|
+
end
|
80
102
|
|
81
103
|
def setup
|
82
104
|
rm_r CACHE_DIR if File.exists? CACHE_DIR
|
105
|
+
# force reinitializing cache dirs
|
106
|
+
Rack::Cache::Storage.instance.clear
|
83
107
|
|
84
108
|
request_count = 0
|
85
109
|
response = lambda { |env|
|
@@ -90,6 +114,8 @@ class HttpCachingTest < Test::Unit::TestCase
|
|
90
114
|
}
|
91
115
|
|
92
116
|
@conn = Faraday.new do |b|
|
117
|
+
b.use RackErrorsComplainer
|
118
|
+
|
93
119
|
b.use FaradayMiddleware::RackCompatible, Rack::Cache::Context,
|
94
120
|
:metastore => "file:#{CACHE_DIR}/rack/meta",
|
95
121
|
:entitystore => "file:#{CACHE_DIR}/rack/body",
|
@@ -106,10 +132,17 @@ class HttpCachingTest < Test::Unit::TestCase
|
|
106
132
|
def_delegators :@conn, :get, :post
|
107
133
|
|
108
134
|
def test_cache_get
|
109
|
-
|
135
|
+
response = get('/', :user_agent => 'test')
|
136
|
+
assert_equal 'request:1', response.body
|
137
|
+
assert_equal :get, response.env[:method]
|
138
|
+
assert_equal 200, response.status
|
139
|
+
|
110
140
|
response = get('/', :user_agent => 'test')
|
111
141
|
assert_equal 'request:1', response.body
|
112
142
|
assert_equal 'text/plain', response['content-type']
|
143
|
+
assert_equal :get, response.env[:method]
|
144
|
+
assert response.env[:request].respond_to?(:fetch)
|
145
|
+
assert_equal 200, response.status
|
113
146
|
|
114
147
|
assert_equal 'request:2', post('/').body
|
115
148
|
end
|
@@ -119,4 +152,4 @@ class HttpCachingTest < Test::Unit::TestCase
|
|
119
152
|
assert_equal 'request:2', post('/').body
|
120
153
|
assert_equal 'request:3', post('/').body
|
121
154
|
end
|
122
|
-
end unless defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE
|
155
|
+
end unless defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE # rbx bug #1522
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'faraday_middleware/response/chunked'
|
3
|
+
|
4
|
+
describe FaradayMiddleware::Chunked, :type => :response do
|
5
|
+
context "no transfer-encoding" do
|
6
|
+
it "doesn't change nil body" do
|
7
|
+
expect(process(nil).body).to be_nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it "doesn't change an empty body" do
|
11
|
+
expect(process('').body).to eq('')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "doesn't change a normal body" do
|
15
|
+
expect(process('asdf').body).to eq('asdf')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "transfer-encoding gzip" do
|
20
|
+
let(:headers) { {"transfer-encoding" => "gzip"}}
|
21
|
+
|
22
|
+
it "doesn't change nil body" do
|
23
|
+
expect(process(nil).body).to be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "doesn't change an empty body" do
|
27
|
+
expect(process('').body).to eq('')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "doesn't change a normal body" do
|
31
|
+
expect(process('asdf').body).to eq('asdf')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "transfer-encoding chunked" do
|
36
|
+
let(:headers) { {"transfer-encoding" => "chunked"}}
|
37
|
+
|
38
|
+
it "doesn't change nil body" do
|
39
|
+
expect(process(nil).body).to be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "doesn't change an empty body" do
|
43
|
+
expect(process('').body).to eq('')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "parses a basic chunked body" do
|
47
|
+
expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "parses a chunked body with no ending chunk" do
|
51
|
+
expect(process("10\r\nasdfghjklasdfghj\r\n").body).to eq('asdfghjklasdfghj')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "parses a chunked body with no trailing CRLF on the data chunk" do
|
55
|
+
expect(process("10\r\nasdfghjklasdfghj0\r\n").body).to eq('asdfghjklasdfghj')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "parses a chunked body with an extension" do
|
59
|
+
expect(process("10;foo=bar\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
|
60
|
+
end
|
61
|
+
|
62
|
+
it "parses a chunked body with two extensions" do
|
63
|
+
expect(process("10;foo=bar;bar=baz\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
|
64
|
+
end
|
65
|
+
|
66
|
+
it "parses a chunked body with two chunks" do
|
67
|
+
expect(process("8\r\nasdfghjk\r\n8\r\nlasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "transfer-encoding chunked,chunked" do
|
72
|
+
let(:headers) { {"transfer-encoding" => "chunked,chunked"}}
|
73
|
+
|
74
|
+
it "parses a basic chunked body" do
|
75
|
+
expect(process("10\r\nasdfghjklasdfghj\r\n0\r\n").body).to eq('asdfghjklasdfghj')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/encode_json_spec.rb
CHANGED
@@ -17,11 +17,11 @@ describe FaradayMiddleware::EncodeJson do
|
|
17
17
|
let(:result) { process(nil) }
|
18
18
|
|
19
19
|
it "doesn't change body" do
|
20
|
-
result_body.
|
20
|
+
expect(result_body).to be_nil
|
21
21
|
end
|
22
22
|
|
23
23
|
it "doesn't add content type" do
|
24
|
-
result_type.
|
24
|
+
expect(result_type).to be_nil
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -29,11 +29,11 @@ describe FaradayMiddleware::EncodeJson do
|
|
29
29
|
let(:result) { process('') }
|
30
30
|
|
31
31
|
it "doesn't change body" do
|
32
|
-
result_body.
|
32
|
+
expect(result_body).to be_empty
|
33
33
|
end
|
34
34
|
|
35
35
|
it "doesn't add content type" do
|
36
|
-
result_type.
|
36
|
+
expect(result_type).to be_nil
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -41,11 +41,11 @@ describe FaradayMiddleware::EncodeJson do
|
|
41
41
|
let(:result) { process('{"a":1}') }
|
42
42
|
|
43
43
|
it "doesn't change body" do
|
44
|
-
result_body.
|
44
|
+
expect(result_body).to eq('{"a":1}')
|
45
45
|
end
|
46
46
|
|
47
47
|
it "adds content type" do
|
48
|
-
result_type.
|
48
|
+
expect(result_type).to eq('application/json')
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -53,11 +53,11 @@ describe FaradayMiddleware::EncodeJson do
|
|
53
53
|
let(:result) { process({:a => 1}) }
|
54
54
|
|
55
55
|
it "encodes body" do
|
56
|
-
result_body.
|
56
|
+
expect(result_body).to eq('{"a":1}')
|
57
57
|
end
|
58
58
|
|
59
59
|
it "adds content type" do
|
60
|
-
result_type.
|
60
|
+
expect(result_type).to eq('application/json')
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -65,7 +65,7 @@ describe FaradayMiddleware::EncodeJson do
|
|
65
65
|
let(:result) { process({}) }
|
66
66
|
|
67
67
|
it "encodes body" do
|
68
|
-
result_body.
|
68
|
+
expect(result_body).to eq('{}')
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
@@ -73,11 +73,11 @@ describe FaradayMiddleware::EncodeJson do
|
|
73
73
|
let(:result) { process({:a => 1}, 'application/json; charset=utf-8') }
|
74
74
|
|
75
75
|
it "encodes body" do
|
76
|
-
result_body.
|
76
|
+
expect(result_body).to eq('{"a":1}')
|
77
77
|
end
|
78
78
|
|
79
79
|
it "doesn't change content type" do
|
80
|
-
result_type.
|
80
|
+
expect(result_type).to eq('application/json; charset=utf-8')
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
@@ -85,11 +85,11 @@ describe FaradayMiddleware::EncodeJson do
|
|
85
85
|
let(:result) { process({:a => 1}, 'application/xml; charset=utf-8') }
|
86
86
|
|
87
87
|
it "doesn't change body" do
|
88
|
-
result_body.
|
88
|
+
expect(result_body).to eq({:a => 1})
|
89
89
|
end
|
90
90
|
|
91
91
|
it "doesn't change content type" do
|
92
|
-
result_type.
|
92
|
+
expect(result_type).to eq('application/xml; charset=utf-8')
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
@@ -1,33 +1,218 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'faraday_middleware/response/follow_redirects'
|
3
3
|
require 'faraday'
|
4
|
-
|
4
|
+
|
5
|
+
# expose a method in Test adapter that should have been public
|
6
|
+
Faraday::Adapter::Test::Stubs.class_eval { public :new_stub }
|
5
7
|
|
6
8
|
describe FaradayMiddleware::FollowRedirects do
|
7
|
-
let(:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
stub.
|
13
|
-
stub.get('/found')
|
14
|
-
|
9
|
+
let(:middleware_options) { Hash.new }
|
10
|
+
|
11
|
+
shared_examples_for "a successful redirection" do |status_code|
|
12
|
+
it "follows the redirection for a GET request" do
|
13
|
+
expect(connection do |stub|
|
14
|
+
stub.get('/permanent') { [status_code, {'Location' => '/found'}, ''] }
|
15
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
16
|
+
end.get('/permanent').body).to eq 'fin'
|
17
|
+
end
|
18
|
+
|
19
|
+
it "follows the redirection for a HEAD request" do
|
20
|
+
expect(connection do |stub|
|
21
|
+
stub.head('/permanent') { [status_code, {'Location' => '/found'}, ''] }
|
22
|
+
stub.head('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
|
23
|
+
end.head('/permanent').status).to eq 200
|
24
|
+
end
|
25
|
+
|
26
|
+
it "follows the redirection for a OPTIONS request" do
|
27
|
+
expect(connection do |stub|
|
28
|
+
stub.new_stub(:options, '/permanent') { [status_code, {'Location' => '/found'}, ''] }
|
29
|
+
stub.new_stub(:options, '/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
|
30
|
+
end.run_request(:options, '/permanent', nil, nil).status).to eq 200
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
shared_examples_for "a forced GET redirection" do |status_code|
|
35
|
+
[:put, :post, :delete, :patch].each do |method|
|
36
|
+
it "a #{method.to_s.upcase} request is converted to a GET" do
|
37
|
+
expect(connection do |stub|
|
38
|
+
stub.new_stub(method, '/redirect') {
|
39
|
+
[status_code, {'Location' => '/found'}, 'elsewhere']
|
40
|
+
}
|
41
|
+
stub.get('/found') { |env|
|
42
|
+
body = env[:body] and body.empty? && (body = nil)
|
43
|
+
[200, {'Content-Type' => 'text/plain'}, body.inspect]
|
44
|
+
}
|
45
|
+
end.run_request(method, '/redirect', 'request data', nil).body).to eq('nil')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
shared_examples_for "a replayed redirection" do |status_code|
|
51
|
+
it "redirects with the original request headers" do
|
52
|
+
conn = connection do |stub|
|
53
|
+
stub.get('/redirect') {
|
54
|
+
[status_code, {'Location' => '/found'}, '']
|
55
|
+
}
|
56
|
+
stub.get('/found') { |env|
|
57
|
+
[200, {'Content-Type' => 'text/plain'}, env[:request_headers]['X-Test-Value']]
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
response = conn.get('/redirect') { |req|
|
62
|
+
req.headers['X-Test-Value'] = 'success'
|
63
|
+
}
|
64
|
+
|
65
|
+
expect(response.body).to eq('success')
|
66
|
+
end
|
67
|
+
|
68
|
+
[:put, :post, :delete, :patch].each do |method|
|
69
|
+
it "replays a #{method.to_s.upcase} request" do
|
70
|
+
expect(connection do |stub|
|
71
|
+
stub.new_stub(method, '/redirect') { [status_code, {'Location' => '/found'}, ''] }
|
72
|
+
stub.new_stub(method, '/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
73
|
+
end.run_request(method, '/redirect', nil, nil).body).to eq 'fin'
|
15
74
|
end
|
16
75
|
end
|
17
|
-
}
|
18
76
|
|
19
|
-
|
20
|
-
|
77
|
+
[:put, :post, :patch].each do |method|
|
78
|
+
it "forwards request body for a #{method.to_s.upcase} request" do
|
79
|
+
conn = connection do |stub|
|
80
|
+
stub.new_stub(method, '/redirect') {
|
81
|
+
[status_code, {'Location' => '/found'}, '']
|
82
|
+
}
|
83
|
+
stub.new_stub(method, '/found') { |env|
|
84
|
+
[200, {'Content-Type' => 'text/plain'}, env[:body]]
|
85
|
+
}
|
86
|
+
end
|
21
87
|
|
22
|
-
|
23
|
-
|
88
|
+
response = conn.run_request(method, '/redirect', 'original data', nil)
|
89
|
+
expect(response.body).to eq('original data')
|
90
|
+
end
|
91
|
+
end
|
24
92
|
end
|
25
93
|
|
26
|
-
|
27
|
-
|
94
|
+
|
95
|
+
it "returns non-redirect response results" do
|
96
|
+
expect(connection do |stub|
|
97
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
98
|
+
end.get('/found').body).to eq 'fin'
|
99
|
+
end
|
100
|
+
|
101
|
+
it "follows a single redirection" do
|
102
|
+
expect(connection do |stub|
|
103
|
+
stub.get('/') { [301, {'Location' => '/found'}, ''] }
|
104
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
105
|
+
end.get('/').body).to eq 'fin'
|
28
106
|
end
|
29
107
|
|
30
|
-
it "
|
31
|
-
expect
|
108
|
+
it "follows many redirections" do
|
109
|
+
expect(connection do |stub|
|
110
|
+
stub.get('/') { [301, {'Location' => '/redirect1'}, ''] }
|
111
|
+
stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] }
|
112
|
+
stub.get('/redirect2') { [301, {'Location' => '/found'}, ''] }
|
113
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
114
|
+
end.get('/').body).to eq 'fin'
|
115
|
+
end
|
116
|
+
|
117
|
+
it "raises a FaradayMiddleware::RedirectLimitReached after 3 redirections (by default)" do
|
118
|
+
conn = connection do |stub|
|
119
|
+
stub.get('/') { [301, {'Location' => '/redirect1'}, ''] }
|
120
|
+
stub.get('/redirect1') { [301, {'Location' => '/redirect2'}, ''] }
|
121
|
+
stub.get('/redirect2') { [301, {'Location' => '/redirect3'}, ''] }
|
122
|
+
stub.get('/redirect3') { [301, {'Location' => '/found'}, ''] }
|
123
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
124
|
+
end
|
125
|
+
|
126
|
+
expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "raises a FaradayMiddleware::RedirectLimitReached after the initialized limit" do
|
130
|
+
conn = connection(:limit => 1) do |stub|
|
131
|
+
stub.get('/') { [301, {'Location' => '/redirect1'}, ''] }
|
132
|
+
stub.get('/redirect1') { [301, {'Location' => '/found'}, ''] }
|
133
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
134
|
+
end
|
135
|
+
|
136
|
+
expect{ conn.get('/') }.to raise_error(FaradayMiddleware::RedirectLimitReached)
|
137
|
+
end
|
138
|
+
|
139
|
+
context "when cookies option" do
|
140
|
+
|
141
|
+
let(:cookies) { 'cookie1=abcdefg; cookie2=1234567; cookie3=awesome' }
|
142
|
+
|
143
|
+
context "is :all" do
|
144
|
+
it "puts all cookies from the response into the next request" do
|
145
|
+
expect(connection(:cookies => :all) do |stub|
|
146
|
+
stub.get('/') { [301, {'Location' => '/found', 'Cookies' => cookies }, ''] }
|
147
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
|
148
|
+
end.get('/').env[:request_headers][:cookies]).to eq(cookies)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "not set cookies header on request when response has no cookies" do
|
152
|
+
expect(connection(:cookies => :all) do |stub|
|
153
|
+
stub.get('/') { [301, {'Location' => '/found'}, ''] }
|
154
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
|
155
|
+
end.get('/').env[:request_headers].has_key?('Cookies')).to eq(false)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "is an array of cookie names" do
|
160
|
+
it "puts selected cookies from the response into the next request" do
|
161
|
+
expect(connection(:cookies => ['cookie2']) do |stub|
|
162
|
+
stub.get('/') { [301, {'Location' => '/found', 'Cookies' => cookies }, ''] }
|
163
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, ''] }
|
164
|
+
end.get('/').env[:request_headers][:cookies]).to eq('cookie2=1234567')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context "for an HTTP 301 response" do
|
170
|
+
it_behaves_like 'a successful redirection', 301
|
171
|
+
it_behaves_like 'a forced GET redirection', 301
|
172
|
+
end
|
173
|
+
|
174
|
+
context "for an HTTP 302 response" do
|
175
|
+
it_behaves_like 'a successful redirection', 302
|
176
|
+
|
177
|
+
context "by default" do
|
178
|
+
it_behaves_like 'a forced GET redirection', 302
|
179
|
+
end
|
180
|
+
|
181
|
+
context "with standards compliancy enabled" do
|
182
|
+
let(:middleware_options) { { :standards_compliant => true } }
|
183
|
+
it_behaves_like 'a replayed redirection', 302
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "for an HTTP 303 response" do
|
188
|
+
it_behaves_like 'a successful redirection', 303
|
189
|
+
it_behaves_like 'a forced GET redirection', 303
|
190
|
+
end
|
191
|
+
|
192
|
+
context "for an HTTP 307 response" do
|
193
|
+
it_behaves_like 'a successful redirection', 307
|
194
|
+
it_behaves_like 'a replayed redirection', 307
|
195
|
+
end
|
196
|
+
|
197
|
+
# checks env hash in request phase for basic validity
|
198
|
+
class Lint < Struct.new(:app)
|
199
|
+
def call(env)
|
200
|
+
if env[:status] or env[:response] or env[:response_headers]
|
201
|
+
raise "invalid request: #{env.inspect}"
|
202
|
+
end
|
203
|
+
app.call(env)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def connection(options = middleware_options)
|
210
|
+
Faraday.new do |c|
|
211
|
+
c.use described_class, options
|
212
|
+
c.use Lint
|
213
|
+
c.adapter :test do |stub|
|
214
|
+
yield(stub) if block_given?
|
215
|
+
end
|
216
|
+
end
|
32
217
|
end
|
33
218
|
end
|