excon 0.0.14 → 0.0.15
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of excon might be problematic. Click here for more details.
- data/Rakefile +2 -19
- data/VERSION +1 -1
- data/benchmarks/excon_vs.rb +1 -1
- data/excon.gemspec +2 -4
- data/lib/excon.rb +1 -21
- data/lib/excon/connection.rb +100 -145
- data/tests/config.ru +1 -1
- data/tests/test_helper.rb +3 -3
- metadata +2 -4
- data/Gemfile +0 -14
- data/tests/mock_tests.rb +0 -40
data/Rakefile
CHANGED
@@ -16,25 +16,8 @@ rescue LoadError
|
|
16
16
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
17
|
end
|
18
18
|
|
19
|
-
require 'rake
|
20
|
-
Rake
|
21
|
-
test.libs << 'lib' << 'test'
|
22
|
-
test.pattern = 'test/**/*_test.rb'
|
23
|
-
test.verbose = true
|
24
|
-
end
|
25
|
-
|
26
|
-
begin
|
27
|
-
require 'rcov/rcovtask'
|
28
|
-
Rcov::RcovTask.new do |test|
|
29
|
-
test.libs << 'test'
|
30
|
-
test.pattern = 'test/**/*_test.rb'
|
31
|
-
test.verbose = true
|
32
|
-
end
|
33
|
-
rescue LoadError
|
34
|
-
task :rcov do
|
35
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
36
|
-
end
|
37
|
-
end
|
19
|
+
require 'shindo/rake'
|
20
|
+
Shindo::Rake.new
|
38
21
|
|
39
22
|
task :test => :check_dependencies
|
40
23
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.15
|
data/benchmarks/excon_vs.rb
CHANGED
data/excon.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{excon}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.15"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Wesley Beary"]
|
12
|
-
s.date = %q{2010-01-
|
12
|
+
s.date = %q{2010-01-06}
|
13
13
|
s.description = %q{speed, persistence, http(s)}
|
14
14
|
s.email = %q{wbeary@engineyard.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -18,7 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.files = [
|
19
19
|
".document",
|
20
20
|
".gitignore",
|
21
|
-
"Gemfile",
|
22
21
|
"README.rdoc",
|
23
22
|
"Rakefile",
|
24
23
|
"VERSION",
|
@@ -31,7 +30,6 @@ Gem::Specification.new do |s|
|
|
31
30
|
"lib/excon/errors.rb",
|
32
31
|
"lib/excon/response.rb",
|
33
32
|
"tests/config.ru",
|
34
|
-
"tests/mock_tests.rb",
|
35
33
|
"tests/test_helper.rb",
|
36
34
|
"tests/threaded_tests.rb"
|
37
35
|
]
|
data/lib/excon.rb
CHANGED
@@ -8,6 +8,7 @@ require 'openssl'
|
|
8
8
|
require 'socket'
|
9
9
|
require 'uri'
|
10
10
|
|
11
|
+
require 'excon/connection'
|
11
12
|
require 'excon/errors'
|
12
13
|
require 'excon/response'
|
13
14
|
|
@@ -15,29 +16,8 @@ module Excon
|
|
15
16
|
|
16
17
|
CHUNK_SIZE = 1048576 # 1 megabyte
|
17
18
|
|
18
|
-
def self.reload
|
19
|
-
load 'excon/connection.rb'
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.mock!
|
23
|
-
@mocking = true
|
24
|
-
@mocks = {}
|
25
|
-
|
26
|
-
def self.mocks
|
27
|
-
@mocks
|
28
|
-
end
|
29
|
-
|
30
|
-
self.reload
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.mocking?
|
34
|
-
!!@mocking
|
35
|
-
end
|
36
|
-
|
37
19
|
def self.new(url)
|
38
20
|
Excon::Connection.new(url)
|
39
21
|
end
|
40
22
|
|
41
23
|
end
|
42
|
-
|
43
|
-
Excon.reload
|
data/lib/excon/connection.rb
CHANGED
@@ -1,175 +1,130 @@
|
|
1
|
-
|
1
|
+
module Excon
|
2
|
+
class Connection
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
def initialize(url)
|
7
|
-
@uri = URI.parse(url)
|
8
|
-
Thread.current[:_excon_connections] ||= {}
|
9
|
-
end
|
4
|
+
def initialize(url)
|
5
|
+
@uri = URI.parse(url)
|
6
|
+
end
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
8
|
+
def request(params)
|
9
|
+
begin
|
10
|
+
params[:path] ||= ''
|
11
|
+
unless params[:path][0..0] == '/'
|
12
|
+
params[:path] = "/#{params[:path]}"
|
13
|
+
end
|
14
|
+
if params[:query] && !params[:query].empty?
|
15
|
+
params[:path] << "?#{params[:query]}"
|
16
|
+
end
|
17
|
+
request = "#{params[:method]} #{params[:path]} HTTP/1.1\r\n"
|
18
|
+
params[:headers] ||= {}
|
19
|
+
params[:headers]['Host'] = params[:host] || @uri.host
|
20
|
+
if params[:body] && !params[:headers]['Content-Length']
|
21
|
+
params[:headers]['Content-Length'] = params[:body].length
|
22
|
+
end
|
23
|
+
for key, value in params[:headers]
|
24
|
+
request << "#{key}: #{value}\r\n"
|
25
|
+
end
|
26
|
+
request << "\r\n"
|
27
|
+
socket.write(request)
|
28
|
+
|
29
|
+
if params[:body]
|
30
|
+
if params[:body].is_a?(String)
|
31
|
+
socket.write(params[:body])
|
32
|
+
else
|
33
|
+
while chunk = params[:body].read(CHUNK_SIZE)
|
34
|
+
socket.write(chunk)
|
35
|
+
end
|
28
36
|
end
|
29
|
-
|
30
|
-
connection.write(request)
|
37
|
+
end
|
31
38
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
response = Excon::Response.new
|
40
|
+
response.status = socket.readline[9..11].to_i
|
41
|
+
while true
|
42
|
+
data = socket.readline.chop!
|
43
|
+
unless data.empty?
|
44
|
+
key, value = data.split(': ')
|
45
|
+
response.headers[key] = value
|
46
|
+
else
|
47
|
+
break
|
40
48
|
end
|
49
|
+
end
|
41
50
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
response.headers[key] = value
|
49
|
-
else
|
50
|
-
break
|
51
|
-
end
|
51
|
+
unless params[:method] == 'HEAD'
|
52
|
+
block = if !params[:block] || (params[:expects] && ![*params[:expects]].include?(response.status))
|
53
|
+
response.body = ''
|
54
|
+
lambda { |chunk| response.body << chunk }
|
55
|
+
else
|
56
|
+
params[:block]
|
52
57
|
end
|
53
58
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
params[:block]
|
59
|
+
if response.headers['Content-Length']
|
60
|
+
remaining = response.headers['Content-Length'].to_i
|
61
|
+
while remaining > 0
|
62
|
+
block.call(socket.read([CHUNK_SIZE, remaining].min))
|
63
|
+
remaining -= CHUNK_SIZE
|
60
64
|
end
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
while true
|
70
|
-
chunk_size = connection.readline.chop!.to_i(16)
|
71
|
-
chunk = connection.read(chunk_size + 2).chop! # 2 == "/r/n".length
|
72
|
-
if chunk_size > 0
|
73
|
-
block.call(chunk)
|
74
|
-
else
|
75
|
-
break
|
76
|
-
end
|
65
|
+
elsif response.headers['Transfer-Encoding'] == 'chunked'
|
66
|
+
while true
|
67
|
+
chunk_size = socket.readline.chop!.to_i(16)
|
68
|
+
chunk = socket.read(chunk_size + 2).chop! # 2 == "/r/n".length
|
69
|
+
if chunk_size > 0
|
70
|
+
block.call(chunk)
|
71
|
+
else
|
72
|
+
break
|
77
73
|
end
|
78
|
-
elsif response.headers['Connection'] == 'close'
|
79
|
-
block.call(connection.read)
|
80
|
-
Thread.current[:_excon_connections][@uri.to_s] = nil
|
81
74
|
end
|
75
|
+
elsif response.headers['Connection'] == 'close'
|
76
|
+
block.call(socket.read)
|
77
|
+
reset_socket
|
82
78
|
end
|
83
|
-
rescue => connection_error
|
84
|
-
Thread.current[:_excon_connections][@uri.to_s] = nil
|
85
|
-
raise(connection_error)
|
86
|
-
end
|
87
|
-
|
88
|
-
if params[:expects] && ![*params[:expects]].include?(response.status)
|
89
|
-
Thread.current[:_excon_connections][@uri.to_s] = nil
|
90
|
-
raise(Excon::Errors.status_error(params, response))
|
91
|
-
else
|
92
|
-
response
|
93
|
-
end
|
94
|
-
|
95
|
-
rescue => request_error
|
96
|
-
if params[:idempotent] &&
|
97
|
-
(!request_error.is_a?(Excon::Errors::Error) || response.status != 404)
|
98
|
-
retries_remaining ||= 4
|
99
|
-
retries_remaining -= 1
|
100
|
-
if retries_remaining > 0
|
101
|
-
retry
|
102
|
-
end
|
103
|
-
else
|
104
|
-
raise(request_error)
|
105
79
|
end
|
80
|
+
rescue => socket_error
|
81
|
+
reset_socket
|
82
|
+
raise(socket_error)
|
106
83
|
end
|
107
84
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
114
|
-
Thread.current[:_excon_connections][@uri.to_s]
|
85
|
+
if params[:expects] && ![*params[:expects]].include?(response.status)
|
86
|
+
reset_socket
|
87
|
+
raise(Excon::Errors.status_error(params, response))
|
88
|
+
else
|
89
|
+
response
|
115
90
|
end
|
116
91
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
connection.sync_close = true
|
125
|
-
connection.connect
|
92
|
+
rescue => request_error
|
93
|
+
if params[:idempotent] &&
|
94
|
+
(!request_error.is_a?(Excon::Errors::Error) || response.status != 404)
|
95
|
+
retries_remaining ||= 4
|
96
|
+
retries_remaining -= 1
|
97
|
+
if retries_remaining > 0
|
98
|
+
retry
|
126
99
|
end
|
127
|
-
|
128
|
-
|
100
|
+
else
|
101
|
+
raise(request_error)
|
129
102
|
end
|
130
103
|
end
|
131
|
-
end
|
132
104
|
|
133
|
-
|
105
|
+
private
|
134
106
|
|
135
|
-
|
136
|
-
|
107
|
+
def reset_socket
|
108
|
+
new_socket = TCPSocket.open(@uri.host, @uri.port)
|
137
109
|
|
138
|
-
|
110
|
+
if @uri.scheme == 'https'
|
111
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
112
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
113
|
+
new_socket = OpenSSL::SSL::SSLSocket.new(new_socket, @ssl_context)
|
114
|
+
new_socket.sync_close = true
|
115
|
+
new_socket.connect
|
139
116
|
end
|
140
117
|
|
141
|
-
|
142
|
-
|
143
|
-
if key == params
|
144
|
-
response = Excon.mocks[key]
|
145
|
-
break
|
146
|
-
end
|
147
|
-
end
|
148
|
-
unless response
|
149
|
-
response = Excon::Response.new
|
150
|
-
response.status = 404
|
151
|
-
response.headers = { 'Content-Length' => 0 }
|
152
|
-
end
|
153
|
-
|
154
|
-
if params[:expects] && ![*params[:expects]].include?(response.status)
|
155
|
-
raise(Excon::Errors.status_error(params[:expects], response.status, response))
|
156
|
-
else
|
157
|
-
response
|
158
|
-
end
|
118
|
+
Thread.current[:_excon_sockets][@uri.to_s] = new_socket
|
119
|
+
end
|
159
120
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
if retries_remaining > 0
|
165
|
-
retry
|
166
|
-
end
|
167
|
-
else
|
168
|
-
raise(request_error)
|
169
|
-
end
|
121
|
+
def socket
|
122
|
+
Thread.current[:_excon_sockets] ||= {}
|
123
|
+
if !Thread.current[:_excon_sockets][@uri.to_s] || Thread.current[:_excon_sockets][@uri.to_s].closed?
|
124
|
+
reset_socket
|
170
125
|
end
|
171
|
-
|
126
|
+
Thread.current[:_excon_sockets][@uri.to_s]
|
172
127
|
end
|
173
|
-
end
|
174
128
|
|
129
|
+
end
|
175
130
|
end
|
data/tests/config.ru
CHANGED
data/tests/test_helper.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib/excon'))
|
2
2
|
|
3
|
-
|
3
|
+
require 'open4'
|
4
4
|
|
5
5
|
def local_file(*parts)
|
6
6
|
File.expand_path(File.join(File.dirname(__FILE__), *parts))
|
7
7
|
end
|
8
8
|
|
9
|
-
def with_rackup(configru = local_file('config.ru')
|
10
|
-
pid, w, r, e = Open4.popen4("
|
9
|
+
def with_rackup(configru = local_file('config.ru'))
|
10
|
+
pid, w, r, e = Open4.popen4("rackup #{configru}")
|
11
11
|
while `lsof -p #{pid} -P -i | grep ruby | grep TCP`.chomp.empty?; end
|
12
12
|
yield
|
13
13
|
Process.kill(9, pid)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: excon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wesley Beary
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-06 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -24,7 +24,6 @@ extra_rdoc_files:
|
|
24
24
|
files:
|
25
25
|
- .document
|
26
26
|
- .gitignore
|
27
|
-
- Gemfile
|
28
27
|
- README.rdoc
|
29
28
|
- Rakefile
|
30
29
|
- VERSION
|
@@ -37,7 +36,6 @@ files:
|
|
37
36
|
- lib/excon/errors.rb
|
38
37
|
- lib/excon/response.rb
|
39
38
|
- tests/config.ru
|
40
|
-
- tests/mock_tests.rb
|
41
39
|
- tests/test_helper.rb
|
42
40
|
- tests/threaded_tests.rb
|
43
41
|
has_rdoc: true
|
data/Gemfile
DELETED
data/tests/mock_tests.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
-
|
3
|
-
Excon.mock!
|
4
|
-
|
5
|
-
request = {
|
6
|
-
:host => 'www.google.com',
|
7
|
-
:method => 'GET',
|
8
|
-
:path => '/'
|
9
|
-
}
|
10
|
-
response = Excon::Response.new({
|
11
|
-
:status => 200,
|
12
|
-
:headers => { 'Content-Length' => '11' },
|
13
|
-
:body => 'Hello World'
|
14
|
-
})
|
15
|
-
Excon.mocks[request] = response
|
16
|
-
|
17
|
-
Shindo.tests do
|
18
|
-
|
19
|
-
before do
|
20
|
-
@connection = Excon.new('http://www.google.com')
|
21
|
-
@response = @connection.request({
|
22
|
-
:host => 'www.google.com',
|
23
|
-
:method => 'GET',
|
24
|
-
:path => '/'
|
25
|
-
})
|
26
|
-
end
|
27
|
-
|
28
|
-
test("status => 200") do
|
29
|
-
@response.status == 200
|
30
|
-
end
|
31
|
-
|
32
|
-
test("headers => { 'Content-Length' => '11' }") do
|
33
|
-
@response.headers == { 'Content-Length' => '11' }
|
34
|
-
end
|
35
|
-
|
36
|
-
test("body => 'Hello World") do
|
37
|
-
@response.body == 'Hello World'
|
38
|
-
end
|
39
|
-
|
40
|
-
end
|