em-http-request-samesite 1.1.7
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 +7 -0
- data/.gemtest +0 -0
- data/.gitignore +9 -0
- data/.rspec +0 -0
- data/.travis.yml +7 -0
- data/Changelog.md +68 -0
- data/Gemfile +14 -0
- data/README.md +63 -0
- data/Rakefile +10 -0
- data/benchmarks/clients.rb +170 -0
- data/benchmarks/em-excon.rb +87 -0
- data/benchmarks/em-profile.gif +0 -0
- data/benchmarks/em-profile.txt +65 -0
- data/benchmarks/server.rb +48 -0
- data/em-http-request.gemspec +32 -0
- data/examples/.gitignore +1 -0
- data/examples/digest_auth/client.rb +25 -0
- data/examples/digest_auth/server.rb +28 -0
- data/examples/fetch.rb +30 -0
- data/examples/fibered-http.rb +51 -0
- data/examples/multi.rb +25 -0
- data/examples/oauth-tweet.rb +35 -0
- data/examples/socks5.rb +23 -0
- data/lib/em-http-request.rb +1 -0
- data/lib/em-http.rb +20 -0
- data/lib/em-http/client.rb +341 -0
- data/lib/em-http/core_ext/bytesize.rb +6 -0
- data/lib/em-http/decoders.rb +252 -0
- data/lib/em-http/http_client_options.rb +49 -0
- data/lib/em-http/http_connection.rb +321 -0
- data/lib/em-http/http_connection_options.rb +70 -0
- data/lib/em-http/http_encoding.rb +149 -0
- data/lib/em-http/http_header.rb +83 -0
- data/lib/em-http/http_status_codes.rb +57 -0
- data/lib/em-http/middleware/digest_auth.rb +112 -0
- data/lib/em-http/middleware/json_response.rb +15 -0
- data/lib/em-http/middleware/oauth.rb +40 -0
- data/lib/em-http/middleware/oauth2.rb +28 -0
- data/lib/em-http/multi.rb +57 -0
- data/lib/em-http/request.rb +23 -0
- data/lib/em-http/version.rb +5 -0
- data/lib/em/io_streamer.rb +49 -0
- data/spec/client_fiber_spec.rb +23 -0
- data/spec/client_spec.rb +1000 -0
- data/spec/digest_auth_spec.rb +48 -0
- data/spec/dns_spec.rb +41 -0
- data/spec/encoding_spec.rb +49 -0
- data/spec/external_spec.rb +150 -0
- data/spec/fixtures/google.ca +16 -0
- data/spec/fixtures/gzip-sample.gz +0 -0
- data/spec/gzip_spec.rb +91 -0
- data/spec/helper.rb +31 -0
- data/spec/http_proxy_spec.rb +268 -0
- data/spec/middleware/oauth2_spec.rb +15 -0
- data/spec/middleware_spec.rb +143 -0
- data/spec/multi_spec.rb +104 -0
- data/spec/pipelining_spec.rb +66 -0
- data/spec/redirect_spec.rb +430 -0
- data/spec/socksify_proxy_spec.rb +60 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/ssl_spec.rb +71 -0
- data/spec/stallion.rb +334 -0
- data/spec/stub_server.rb +45 -0
- metadata +265 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
describe EventMachine::Middleware::OAuth2 do
|
2
|
+
it "should add an access token to a URI with no query parameters" do
|
3
|
+
middleware = EventMachine::Middleware::OAuth2.new(:access_token => "fedcba9876543210")
|
4
|
+
uri = Addressable::URI.parse("https://graph.facebook.com/me")
|
5
|
+
middleware.update_uri! uri
|
6
|
+
uri.to_s.should == "https://graph.facebook.com/me?access_token=fedcba9876543210"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should add an access token to a URI with query parameters" do
|
10
|
+
middleware = EventMachine::Middleware::OAuth2.new(:access_token => "fedcba9876543210")
|
11
|
+
uri = Addressable::URI.parse("https://graph.facebook.com/me?fields=photo")
|
12
|
+
middleware.update_uri! uri
|
13
|
+
uri.to_s.should == "https://graph.facebook.com/me?fields=photo&access_token=fedcba9876543210"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe EventMachine::HttpRequest do
|
4
|
+
|
5
|
+
class EmptyMiddleware; end
|
6
|
+
|
7
|
+
class GlobalMiddleware
|
8
|
+
def response(resp)
|
9
|
+
resp.response_header['X-Global'] = 'middleware'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should accept middleware" do
|
14
|
+
EventMachine.run {
|
15
|
+
lambda {
|
16
|
+
conn = EM::HttpRequest.new('http://127.0.0.1:8090')
|
17
|
+
conn.use ResponseMiddleware
|
18
|
+
conn.use EmptyMiddleware
|
19
|
+
|
20
|
+
EM.stop
|
21
|
+
}.should_not raise_error
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
context "configuration" do
|
26
|
+
class ConfigurableMiddleware
|
27
|
+
def initialize(conf, &block)
|
28
|
+
@conf = conf
|
29
|
+
@block = block
|
30
|
+
end
|
31
|
+
|
32
|
+
def response(resp)
|
33
|
+
resp.response_header['X-Conf'] = @conf
|
34
|
+
resp.response_header['X-Block'] = @block.call
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should accept middleware initialization parameters" do
|
39
|
+
EventMachine.run {
|
40
|
+
conn = EM::HttpRequest.new('http://127.0.0.1:8090')
|
41
|
+
conn.use ConfigurableMiddleware, 'conf-value' do
|
42
|
+
'block-value'
|
43
|
+
end
|
44
|
+
|
45
|
+
req = conn.get
|
46
|
+
req.callback {
|
47
|
+
req.response_header['X-Conf'].should match('conf-value')
|
48
|
+
req.response_header['X-Block'].should match('block-value')
|
49
|
+
EM.stop
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "request" do
|
56
|
+
class ResponseMiddleware
|
57
|
+
def response(resp)
|
58
|
+
resp.response_header['X-Header'] = 'middleware'
|
59
|
+
resp.response = 'Hello, Middleware!'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should execute response middleware before user callbacks" do
|
64
|
+
EventMachine.run {
|
65
|
+
conn = EM::HttpRequest.new('http://127.0.0.1:8090')
|
66
|
+
conn.use ResponseMiddleware
|
67
|
+
|
68
|
+
req = conn.get
|
69
|
+
req.callback {
|
70
|
+
req.response_header['X-Header'].should match('middleware')
|
71
|
+
req.response.should match('Hello, Middleware!')
|
72
|
+
EM.stop
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should execute global response middleware before user callbacks" do
|
78
|
+
EventMachine.run {
|
79
|
+
EM::HttpRequest.use GlobalMiddleware
|
80
|
+
|
81
|
+
conn = EM::HttpRequest.new('http://127.0.0.1:8090')
|
82
|
+
|
83
|
+
req = conn.get
|
84
|
+
req.callback {
|
85
|
+
req.response_header['X-Global'].should match('middleware')
|
86
|
+
EM.stop
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "request" do
|
93
|
+
class RequestMiddleware
|
94
|
+
def request(client, head, body)
|
95
|
+
head['X-Middleware'] = 'middleware' # insert new header
|
96
|
+
body += ' modified' # modify post body
|
97
|
+
|
98
|
+
[head, body]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should execute request middleware before dispatching request" do
|
103
|
+
EventMachine.run {
|
104
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
|
105
|
+
conn.use RequestMiddleware
|
106
|
+
|
107
|
+
req = conn.post :body => "data"
|
108
|
+
req.callback {
|
109
|
+
req.response_header.status.should == 200
|
110
|
+
req.response.should match(/data modified/)
|
111
|
+
EventMachine.stop
|
112
|
+
}
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "jsonify" do
|
118
|
+
class JSONify
|
119
|
+
def request(client, head, body)
|
120
|
+
[head, MultiJson.dump(body)]
|
121
|
+
end
|
122
|
+
|
123
|
+
def response(resp)
|
124
|
+
resp.response = MultiJson.load(resp.response)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should use middleware to JSON encode and JSON decode the body" do
|
129
|
+
EventMachine.run {
|
130
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/')
|
131
|
+
conn.use JSONify
|
132
|
+
|
133
|
+
req = conn.post :body => {:ruby => :hash}
|
134
|
+
req.callback {
|
135
|
+
req.response_header.status.should == 200
|
136
|
+
req.response.should == {"ruby" => "hash"}
|
137
|
+
EventMachine.stop
|
138
|
+
}
|
139
|
+
}
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
data/spec/multi_spec.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'stallion'
|
3
|
+
|
4
|
+
describe EventMachine::MultiRequest do
|
5
|
+
|
6
|
+
let(:multi) { EventMachine::MultiRequest.new }
|
7
|
+
let(:url) { 'http://127.0.0.1:8090/' }
|
8
|
+
|
9
|
+
it "should submit multiple requests in parallel and return once all of them are complete" do
|
10
|
+
EventMachine.run {
|
11
|
+
multi.add :a, EventMachine::HttpRequest.new(url).get
|
12
|
+
multi.add :b, EventMachine::HttpRequest.new(url).post
|
13
|
+
multi.add :c, EventMachine::HttpRequest.new(url).head
|
14
|
+
multi.add :d, EventMachine::HttpRequest.new(url).delete
|
15
|
+
multi.add :e, EventMachine::HttpRequest.new(url).put
|
16
|
+
|
17
|
+
multi.callback {
|
18
|
+
multi.responses[:callback].size.should == 5
|
19
|
+
multi.responses[:callback].each { |name, response|
|
20
|
+
[ :a, :b, :c, :d, :e ].should include(name)
|
21
|
+
response.response_header.status.should == 200
|
22
|
+
}
|
23
|
+
multi.responses[:errback].size.should == 0
|
24
|
+
|
25
|
+
EventMachine.stop
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should require unique keys for each deferrable" do
|
31
|
+
lambda do
|
32
|
+
multi.add :df1, EM::DefaultDeferrable.new
|
33
|
+
multi.add :df1, EM::DefaultDeferrable.new
|
34
|
+
end.should raise_error("Duplicate Multi key")
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
describe "#requests" do
|
39
|
+
it "should return the added requests" do
|
40
|
+
request1 = double('request1', :callback => nil, :errback => nil)
|
41
|
+
request2 = double('request2', :callback => nil, :errback => nil)
|
42
|
+
|
43
|
+
multi.add :a, request1
|
44
|
+
multi.add :b, request2
|
45
|
+
|
46
|
+
multi.requests.should == {:a => request1, :b => request2}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#responses" do
|
51
|
+
it "should have an empty :callback hash" do
|
52
|
+
multi.responses[:callback].should be_a(Hash)
|
53
|
+
multi.responses[:callback].size.should == 0
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should have an empty :errback hash" do
|
57
|
+
multi.responses[:errback].should be_a(Hash)
|
58
|
+
multi.responses[:errback].size.should == 0
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should provide access to the requests by name" do
|
62
|
+
EventMachine.run {
|
63
|
+
request1 = EventMachine::HttpRequest.new(url).get
|
64
|
+
request2 = EventMachine::HttpRequest.new(url).post
|
65
|
+
multi.add :a, request1
|
66
|
+
multi.add :b, request2
|
67
|
+
|
68
|
+
multi.callback {
|
69
|
+
multi.responses[:callback][:a].should equal(request1)
|
70
|
+
multi.responses[:callback][:b].should equal(request2)
|
71
|
+
|
72
|
+
EventMachine.stop
|
73
|
+
}
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#finished?" do
|
79
|
+
it "should be true when no requests have been added" do
|
80
|
+
multi.should be_finished
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should be false while the requests are not finished" do
|
84
|
+
EventMachine.run {
|
85
|
+
multi.add :a, EventMachine::HttpRequest.new(url).get
|
86
|
+
multi.should_not be_finished
|
87
|
+
|
88
|
+
EventMachine.stop
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be finished when all requests are finished" do
|
93
|
+
EventMachine.run {
|
94
|
+
multi.add :a, EventMachine::HttpRequest.new(url).get
|
95
|
+
multi.callback {
|
96
|
+
multi.should be_finished
|
97
|
+
|
98
|
+
EventMachine.stop
|
99
|
+
}
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
requires_connection do
|
4
|
+
|
5
|
+
describe EventMachine::HttpRequest do
|
6
|
+
|
7
|
+
it "should perform successful pipelined GETs" do
|
8
|
+
EventMachine.run do
|
9
|
+
|
10
|
+
# Mongrel doesn't support pipelined requests - bah!
|
11
|
+
conn = EventMachine::HttpRequest.new('http://www.bing.com/')
|
12
|
+
|
13
|
+
pipe1 = conn.get :keepalive => true
|
14
|
+
pipe2 = conn.get :path => '/news', :keepalive => true
|
15
|
+
|
16
|
+
processed = 0
|
17
|
+
stop = proc { EM.stop if processed == 2}
|
18
|
+
|
19
|
+
pipe1.errback { failed(conn) }
|
20
|
+
pipe1.callback {
|
21
|
+
processed += 1
|
22
|
+
pipe1.response_header.status.should == 200
|
23
|
+
stop.call
|
24
|
+
}
|
25
|
+
|
26
|
+
pipe2.errback { failed(conn) }
|
27
|
+
pipe2.callback {
|
28
|
+
processed += 1
|
29
|
+
pipe2.response_header.status.should == 200
|
30
|
+
pipe2.response.should match(/html/i)
|
31
|
+
stop.call
|
32
|
+
}
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should perform successful pipelined HEAD requests" do
|
38
|
+
EventMachine.run do
|
39
|
+
conn = EventMachine::HttpRequest.new('http://www.bing.com/')
|
40
|
+
|
41
|
+
pipe1 = conn.head :keepalive => true
|
42
|
+
pipe2 = conn.head :path => '/news', :keepalive => true
|
43
|
+
|
44
|
+
processed = 0
|
45
|
+
stop = proc { EM.stop if processed == 2}
|
46
|
+
|
47
|
+
pipe1.errback { failed(conn) }
|
48
|
+
pipe1.callback {
|
49
|
+
processed += 1
|
50
|
+
pipe1.response_header.status.should == 200
|
51
|
+
stop.call
|
52
|
+
}
|
53
|
+
|
54
|
+
pipe2.errback { failed(conn) }
|
55
|
+
pipe2.callback {
|
56
|
+
processed += 1
|
57
|
+
pipe2.response_header.status.should == 200
|
58
|
+
stop.call
|
59
|
+
}
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,430 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class RedirectMiddleware
|
4
|
+
attr_reader :call_count
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@call_count = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def request(c, h, r)
|
11
|
+
@call_count += 1
|
12
|
+
[h.merge({'EM-Middleware' => @call_count.to_s}), r]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class PickyRedirectMiddleware < RedirectMiddleware
|
17
|
+
def response(r)
|
18
|
+
if r.redirect? && r.response_header['LOCATION'][-1].chr == '3'
|
19
|
+
# set redirects to 0 to avoid further processing
|
20
|
+
r.req.redirects = 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe EventMachine::HttpRequest do
|
26
|
+
|
27
|
+
it "should follow location redirects" do
|
28
|
+
EventMachine.run {
|
29
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect').get :redirects => 1
|
30
|
+
http.errback { failed(http) }
|
31
|
+
http.callback {
|
32
|
+
http.response_header.status.should == 200
|
33
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
34
|
+
http.response.should == "compressed"
|
35
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/gzip'
|
36
|
+
http.redirects.should == 1
|
37
|
+
|
38
|
+
EM.stop
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should not follow redirects on created" do
|
44
|
+
EventMachine.run {
|
45
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/created').get :redirects => 1
|
46
|
+
http.errback { failed(http) }
|
47
|
+
http.callback {
|
48
|
+
http.response_header.status.should == 201
|
49
|
+
http.response.should match(/Hello/)
|
50
|
+
EM.stop
|
51
|
+
}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not forward cookies across domains with http redirect" do
|
56
|
+
|
57
|
+
expires = (Date.today + 2).strftime('%a, %d %b %Y %T GMT')
|
58
|
+
response =<<-HTTP.gsub(/^ +/, '')
|
59
|
+
HTTP/1.1 301 MOVED PERMANENTLY
|
60
|
+
Location: http://localhost:8071/
|
61
|
+
Set-Cookie: foo=bar; expires=#{expires}; path=/; HttpOnly
|
62
|
+
|
63
|
+
HTTP
|
64
|
+
|
65
|
+
EventMachine.run do
|
66
|
+
@stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response)
|
67
|
+
@echo = StubServer.new(:host => 'localhost', :port => 8071, :echo => true)
|
68
|
+
|
69
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1
|
70
|
+
|
71
|
+
http.errback { failed(http) }
|
72
|
+
http.callback do
|
73
|
+
http.response.should_not match(/Cookie/)
|
74
|
+
@stub.stop
|
75
|
+
@echo.stop
|
76
|
+
EM.stop
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should forward valid cookies across domains with http redirect" do
|
82
|
+
|
83
|
+
expires = (Date.today + 2).strftime('%a, %d %b %Y %T GMT')
|
84
|
+
response =<<-HTTP.gsub(/^ +/, '')
|
85
|
+
HTTP/1.1 301 MOVED PERMANENTLY
|
86
|
+
Location: http://127.0.0.1:8071/
|
87
|
+
Set-Cookie: foo=bar; expires=#{expires}; path=/; HttpOnly
|
88
|
+
|
89
|
+
HTTP
|
90
|
+
|
91
|
+
EventMachine.run do
|
92
|
+
@stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response)
|
93
|
+
@echo = StubServer.new(:host => '127.0.0.1', :port => 8071, :echo => true)
|
94
|
+
|
95
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1
|
96
|
+
|
97
|
+
http.errback { failed(http) }
|
98
|
+
http.callback do
|
99
|
+
http.response.should match(/Cookie/)
|
100
|
+
@stub.stop
|
101
|
+
@echo.stop
|
102
|
+
EM.stop
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
it "should normalize path and forward valid cookies across domains" do
|
109
|
+
|
110
|
+
expires = (Date.today + 2).strftime('%a, %d %b %Y %T GMT')
|
111
|
+
response =<<-HTTP.gsub(/^ +/, '')
|
112
|
+
HTTP/1.1 301 MOVED PERMANENTLY
|
113
|
+
Location: http://127.0.0.1:8071?omg=ponies
|
114
|
+
Set-Cookie: foo=bar; expires=#{expires}; path=/; HttpOnly
|
115
|
+
|
116
|
+
HTTP
|
117
|
+
|
118
|
+
EventMachine.run do
|
119
|
+
@stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response)
|
120
|
+
@echo = StubServer.new(:host => '127.0.0.1', :port => 8071, :echo => true)
|
121
|
+
|
122
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 1
|
123
|
+
|
124
|
+
http.errback { failed(http) }
|
125
|
+
http.callback do
|
126
|
+
http.response.should match(/Cookie/)
|
127
|
+
@stub.stop
|
128
|
+
@echo.stop
|
129
|
+
EM.stop
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should redirect with missing content-length" do
|
135
|
+
EventMachine.run {
|
136
|
+
response = "HTTP/1.0 301 MOVED PERMANENTLY\r\nlocation: http://127.0.0.1:8090/redirect\r\n\r\n"
|
137
|
+
@stub = StubServer.new(:host => '127.0.0.1', :port => 8070, :response => response)
|
138
|
+
|
139
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/').get :redirects => 3
|
140
|
+
http.errback { failed(http) }
|
141
|
+
|
142
|
+
http.callback {
|
143
|
+
http.response_header.status.should == 200
|
144
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
145
|
+
http.response.should == "compressed"
|
146
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/gzip'
|
147
|
+
http.redirects.should == 3
|
148
|
+
|
149
|
+
@stub.stop
|
150
|
+
EM.stop
|
151
|
+
}
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should follow redirects on HEAD method" do
|
156
|
+
EventMachine.run {
|
157
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/head').head :redirects => 1
|
158
|
+
http.errback { failed(http) }
|
159
|
+
http.callback {
|
160
|
+
http.response_header.status.should == 200
|
161
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/'
|
162
|
+
EM.stop
|
163
|
+
}
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should report last_effective_url" do
|
168
|
+
EventMachine.run {
|
169
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/').get
|
170
|
+
http.errback { failed(http) }
|
171
|
+
http.callback {
|
172
|
+
http.response_header.status.should == 200
|
173
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/'
|
174
|
+
|
175
|
+
EM.stop
|
176
|
+
}
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should default to 0 redirects" do
|
181
|
+
EventMachine.run {
|
182
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect').get
|
183
|
+
http.errback { failed(http) }
|
184
|
+
http.callback {
|
185
|
+
http.response_header.status.should == 301
|
186
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/redirect'
|
187
|
+
http.redirects.should == 0
|
188
|
+
|
189
|
+
EM.stop
|
190
|
+
}
|
191
|
+
}
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should not invoke redirect logic on failed(http) connections" do
|
195
|
+
EventMachine.run {
|
196
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8070/', :connect_timeout => 0.1).get :redirects => 5
|
197
|
+
http.callback { failed(http) }
|
198
|
+
http.errback {
|
199
|
+
http.redirects.should == 0
|
200
|
+
EM.stop
|
201
|
+
}
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should normalize redirect urls" do
|
206
|
+
EventMachine.run {
|
207
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/bad').get :redirects => 1
|
208
|
+
http.errback { failed(http) }
|
209
|
+
http.callback {
|
210
|
+
http.last_effective_url.to_s.should match('http://127.0.0.1:8090/')
|
211
|
+
http.response.should match('Hello, World!')
|
212
|
+
EM.stop
|
213
|
+
}
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should fail gracefully on a missing host in absolute Location header" do
|
218
|
+
EventMachine.run {
|
219
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/nohost').get :redirects => 1
|
220
|
+
http.callback { failed(http) }
|
221
|
+
http.errback {
|
222
|
+
http.error.should == 'Location header format error'
|
223
|
+
EM.stop
|
224
|
+
}
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should apply timeout settings on redirects" do
|
229
|
+
EventMachine.run {
|
230
|
+
t = Time.now.to_i
|
231
|
+
EventMachine.heartbeat_interval = 0.1
|
232
|
+
|
233
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/timeout', :inactivity_timeout => 0.1)
|
234
|
+
http = conn.get :redirects => 1
|
235
|
+
http.callback { failed(http) }
|
236
|
+
http.errback {
|
237
|
+
(Time.now.to_i - t).should <= 1
|
238
|
+
EM.stop
|
239
|
+
}
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should capture and pass cookies on redirect and pass_cookies by default" do
|
244
|
+
EventMachine.run {
|
245
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/multiple-with-cookie').get :redirects => 2, :head => {'cookie' => 'id=2;'}
|
246
|
+
http.errback { failed(http) }
|
247
|
+
http.callback {
|
248
|
+
http.response_header.status.should == 200
|
249
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
250
|
+
http.response.should == "compressed"
|
251
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/gzip'
|
252
|
+
http.redirects.should == 2
|
253
|
+
http.cookies.should include("id=2;")
|
254
|
+
http.cookies.should include("another_id=1")
|
255
|
+
|
256
|
+
EM.stop
|
257
|
+
}
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
it "should capture and not pass cookies on redirect if passing is disabled via pass_cookies" do
|
262
|
+
EventMachine.run {
|
263
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/multiple-with-cookie').get :redirects => 2, :pass_cookies => false, :head => {'cookie' => 'id=2;'}
|
264
|
+
http.errback { failed(http) }
|
265
|
+
http.callback {
|
266
|
+
http.response_header.status.should == 200
|
267
|
+
http.response_header["CONTENT_ENCODING"].should == "gzip"
|
268
|
+
http.response.should == "compressed"
|
269
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/gzip'
|
270
|
+
http.redirects.should == 2
|
271
|
+
http.cookies.should include("id=2;")
|
272
|
+
http.cookies.should_not include("another_id=1; expires=Sat, 09 Aug 2031 17:53:39 GMT; path=/;")
|
273
|
+
|
274
|
+
EM.stop
|
275
|
+
}
|
276
|
+
}
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should follow location redirects with path" do
|
280
|
+
EventMachine.run {
|
281
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect').get :path => '/redirect', :redirects => 1
|
282
|
+
http.errback { failed(http) }
|
283
|
+
http.callback {
|
284
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/gzip'
|
285
|
+
http.response_header.status.should == 200
|
286
|
+
http.redirects.should == 1
|
287
|
+
|
288
|
+
EM.stop
|
289
|
+
}
|
290
|
+
}
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should call middleware each time it redirects" do
|
294
|
+
EventMachine.run {
|
295
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/middleware_redirects_1')
|
296
|
+
conn.use RedirectMiddleware
|
297
|
+
http = conn.get :redirects => 3
|
298
|
+
http.errback { failed(http) }
|
299
|
+
http.callback {
|
300
|
+
http.response_header.status.should == 200
|
301
|
+
http.response_header['EM_MIDDLEWARE'].to_i.should == 3
|
302
|
+
EM.stop
|
303
|
+
}
|
304
|
+
}
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should call middleware which may reject a redirection" do
|
308
|
+
EventMachine.run {
|
309
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/middleware_redirects_1')
|
310
|
+
conn.use PickyRedirectMiddleware
|
311
|
+
http = conn.get :redirects => 3
|
312
|
+
http.errback { failed(http) }
|
313
|
+
http.callback {
|
314
|
+
http.response_header.status.should == 301
|
315
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/redirect/middleware_redirects_2'
|
316
|
+
EM.stop
|
317
|
+
}
|
318
|
+
}
|
319
|
+
end
|
320
|
+
|
321
|
+
it "should not add default http port to redirect url that don't include it" do
|
322
|
+
EventMachine.run {
|
323
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/http_no_port')
|
324
|
+
http = conn.get :redirects => 1
|
325
|
+
http.errback {
|
326
|
+
http.last_effective_url.to_s.should == 'http://host/'
|
327
|
+
EM.stop
|
328
|
+
}
|
329
|
+
}
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should not add default https port to redirect url that don't include it" do
|
333
|
+
EventMachine.run {
|
334
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/https_no_port')
|
335
|
+
http = conn.get :redirects => 1
|
336
|
+
http.errback {
|
337
|
+
http.last_effective_url.to_s.should == 'https://host/'
|
338
|
+
EM.stop
|
339
|
+
}
|
340
|
+
}
|
341
|
+
end
|
342
|
+
|
343
|
+
it "should keep default http port in redirect url that include it" do
|
344
|
+
EventMachine.run {
|
345
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/http_with_port')
|
346
|
+
http = conn.get :redirects => 1
|
347
|
+
http.errback {
|
348
|
+
http.last_effective_url.to_s.should == 'http://host:80/'
|
349
|
+
EM.stop
|
350
|
+
}
|
351
|
+
}
|
352
|
+
end
|
353
|
+
|
354
|
+
it "should keep default https port in redirect url that include it" do
|
355
|
+
EventMachine.run {
|
356
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/https_with_port')
|
357
|
+
http = conn.get :redirects => 1
|
358
|
+
http.errback {
|
359
|
+
http.last_effective_url.to_s.should == 'https://host:443/'
|
360
|
+
EM.stop
|
361
|
+
}
|
362
|
+
}
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should ignore query option when redirecting" do
|
366
|
+
EventMachine.run {
|
367
|
+
http = EventMachine::HttpRequest.new('http://127.0.0.1:8090/redirect/ignore_query_option').get :redirects => 1, :query => 'ignore=1'
|
368
|
+
http.errback { failed(http) }
|
369
|
+
http.callback {
|
370
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/redirect/url'
|
371
|
+
http.redirects.should == 1
|
372
|
+
|
373
|
+
redirect_url = http.response
|
374
|
+
redirect_url.should == http.last_effective_url.to_s
|
375
|
+
|
376
|
+
EM.stop
|
377
|
+
}
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
it "should work with keep-alive connections with cross-origin redirect" do
|
382
|
+
Timeout.timeout(1) {
|
383
|
+
EventMachine.run {
|
384
|
+
response =<<-HTTP.gsub(/^ +/, '')
|
385
|
+
HTTP/1.1 301 MOVED PERMANENTLY
|
386
|
+
Location: http://127.0.0.1:8090/
|
387
|
+
Content-Length: 0
|
388
|
+
|
389
|
+
HTTP
|
390
|
+
|
391
|
+
stub_server = StubServer.new(:host => '127.0.0.1', :port => 8070, :keepalive => true, :response => response)
|
392
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8070/', :inactivity_timeout => 60)
|
393
|
+
http = conn.get :redirects => 1, :keepalive => true
|
394
|
+
http.errback { failed(http) }
|
395
|
+
http.callback {
|
396
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8090/'
|
397
|
+
http.redirects.should == 1
|
398
|
+
|
399
|
+
stub_server.stop
|
400
|
+
EM.stop
|
401
|
+
}
|
402
|
+
}
|
403
|
+
}
|
404
|
+
end
|
405
|
+
|
406
|
+
it "should work with keep-alive connections with same-origin redirect" do
|
407
|
+
Timeout.timeout(1) {
|
408
|
+
EventMachine.run {
|
409
|
+
response =<<-HTTP.gsub(/^ +/, '')
|
410
|
+
HTTP/1.1 301 MOVED PERMANENTLY
|
411
|
+
Location: http://127.0.0.1:8070/
|
412
|
+
Content-Length: 0
|
413
|
+
|
414
|
+
HTTP
|
415
|
+
|
416
|
+
stub_server = StubServer.new(:host => '127.0.0.1', :port => 8070, :keepalive => true, :response => response)
|
417
|
+
conn = EventMachine::HttpRequest.new('http://127.0.0.1:8070/', :inactivity_timeout => 60)
|
418
|
+
http = conn.get :redirects => 1, :keepalive => true
|
419
|
+
http.errback { failed(http) }
|
420
|
+
http.callback {
|
421
|
+
http.last_effective_url.to_s.should == 'http://127.0.0.1:8070/'
|
422
|
+
http.redirects.should == 1
|
423
|
+
|
424
|
+
stub_server.stop
|
425
|
+
EM.stop
|
426
|
+
}
|
427
|
+
}
|
428
|
+
}
|
429
|
+
end
|
430
|
+
end
|