excon 0.0.2 → 0.0.3
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/VERSION +1 -1
- data/excon.gemspec +2 -2
- data/lib/excon.rb +23 -1
- data/lib/excon/connection.rb +118 -87
- data/lib/excon/response.rb +4 -3
- data/test/test.rb +16 -1
- metadata +2 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
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.3"
|
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{2009-
|
12
|
+
s.date = %q{2009-11-03}
|
13
13
|
s.description = %q{speed, persistence, http(s)}
|
14
14
|
s.email = %q{wbeary@engineyard.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/excon.rb
CHANGED
@@ -8,14 +8,36 @@ require 'openssl'
|
|
8
8
|
require 'socket'
|
9
9
|
require 'uri'
|
10
10
|
|
11
|
-
require 'excon/connection'
|
12
11
|
require 'excon/errors'
|
13
12
|
require 'excon/response'
|
14
13
|
|
15
14
|
module Excon
|
16
15
|
|
16
|
+
CHUNK_SIZE = 1048576 # 1 megabyte
|
17
|
+
|
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
|
+
|
17
37
|
def self.new(url)
|
18
38
|
Excon::Connection.new(url)
|
19
39
|
end
|
20
40
|
|
21
41
|
end
|
42
|
+
|
43
|
+
Excon.reload
|
data/lib/excon/connection.rb
CHANGED
@@ -1,111 +1,142 @@
|
|
1
|
-
|
2
|
-
class Connection
|
1
|
+
unless Excon.mocking?
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
end
|
3
|
+
module Excon
|
4
|
+
class Connection
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
def initialize(url)
|
7
|
+
@uri = URI.parse(url)
|
8
|
+
end
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
if params[:query] && !params[:query].empty?
|
19
|
-
params[:path] << "?#{params[:query]}"
|
20
|
-
end
|
21
|
-
request = "#{params[:method]} #{params[:path]} HTTP/1.1\r\n"
|
22
|
-
params[:headers] ||= {}
|
23
|
-
params[:headers]['Host'] = params[:host] || @uri.host
|
24
|
-
if params[:body] && !params[:headers]['Content-Length']
|
25
|
-
params[:headers]['Content-Length'] = params[:body].length
|
26
|
-
end
|
27
|
-
for key, value in params[:headers]
|
28
|
-
request << "#{key}: #{value}\r\n"
|
29
|
-
end
|
30
|
-
request << "\r\n"
|
31
|
-
connection.write(request)
|
32
|
-
|
33
|
-
if params[:body]
|
34
|
-
if params[:body].is_a?(String)
|
35
|
-
connection.write(params[:body])
|
36
|
-
else
|
37
|
-
while chunk = params[:body].read(CHUNK_SIZE)
|
38
|
-
connection.write(chunk)
|
39
|
-
end
|
10
|
+
def request(params)
|
11
|
+
begin
|
12
|
+
params[:path] ||= ''
|
13
|
+
unless params[:path][0..0] == '/'
|
14
|
+
params[:path] = "/#{params[:path]}"
|
40
15
|
end
|
41
|
-
|
16
|
+
if params[:query] && !params[:query].empty?
|
17
|
+
params[:path] << "?#{params[:query]}"
|
18
|
+
end
|
19
|
+
request = "#{params[:method]} #{params[:path]} HTTP/1.1\r\n"
|
20
|
+
params[:headers] ||= {}
|
21
|
+
params[:headers]['Host'] = params[:host] || @uri.host
|
22
|
+
if params[:body] && !params[:headers]['Content-Length']
|
23
|
+
params[:headers]['Content-Length'] = params[:body].length
|
24
|
+
end
|
25
|
+
for key, value in params[:headers]
|
26
|
+
request << "#{key}: #{value}\r\n"
|
27
|
+
end
|
28
|
+
request << "\r\n"
|
29
|
+
connection.write(request)
|
42
30
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
break
|
31
|
+
if params[:body]
|
32
|
+
if params[:body].is_a?(String)
|
33
|
+
connection.write(params[:body])
|
34
|
+
else
|
35
|
+
while chunk = params[:body].read(CHUNK_SIZE)
|
36
|
+
connection.write(chunk)
|
37
|
+
end
|
38
|
+
end
|
52
39
|
end
|
53
|
-
end
|
54
40
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
41
|
+
response = Excon::Response.new
|
42
|
+
response.status = connection.readline[9..11].to_i
|
43
|
+
while true
|
44
|
+
data = connection.readline.chop!
|
45
|
+
unless data.empty?
|
46
|
+
header = data.split(': ')
|
47
|
+
response.headers[header[0]] = header[1]
|
48
|
+
else
|
49
|
+
break
|
50
|
+
end
|
59
51
|
end
|
60
52
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
params[:block].
|
65
|
-
remaining -= CHUNK_SIZE
|
53
|
+
unless params[:method] == 'HEAD'
|
54
|
+
if !params[:block] || (params[:expects] && ![*params[:expects]].include?(response.status))
|
55
|
+
response.body = ''
|
56
|
+
params[:block] = lambda { |chunk| response.body << chunk }
|
66
57
|
end
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
58
|
+
|
59
|
+
if response.headers['Content-Length']
|
60
|
+
remaining = response.headers['Content-Length'].to_i
|
61
|
+
while remaining > 0
|
62
|
+
params[:block].call(connection.read([CHUNK_SIZE, remaining].min))
|
63
|
+
remaining -= CHUNK_SIZE
|
64
|
+
end
|
65
|
+
elsif response.headers['Transfer-Encoding'] == 'chunked'
|
66
|
+
while true
|
67
|
+
chunk_size = connection.readline.chomp!.to_i(16)
|
68
|
+
chunk = connection.read(chunk_size + 2).chop! # 2 == "/r/n".length
|
69
|
+
if chunk_size > 0
|
70
|
+
params[:block].call(chunk)
|
71
|
+
else
|
72
|
+
break
|
73
|
+
end
|
75
74
|
end
|
75
|
+
elsif response.headers['Connection'] == 'close'
|
76
|
+
params[:block].call(connection.read)
|
77
|
+
@connection = nil
|
76
78
|
end
|
77
|
-
elsif response.headers['Connection'] == 'close'
|
78
|
-
params[:block].call(connection.read)
|
79
|
-
@connection = nil
|
80
79
|
end
|
80
|
+
rescue => connection_error
|
81
|
+
@connection = nil
|
82
|
+
raise(connection_error)
|
83
|
+
end
|
84
|
+
|
85
|
+
if params[:expects] && ![*params[:expects]].include?(response.status)
|
86
|
+
raise(Excon::Errors.status_error(params[:expects], response.status, response))
|
87
|
+
else
|
88
|
+
response
|
81
89
|
end
|
82
|
-
rescue => connection_error
|
83
|
-
@connection = nil
|
84
|
-
raise(connection_error)
|
85
90
|
end
|
86
91
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
92
|
+
private
|
93
|
+
|
94
|
+
def connection
|
95
|
+
if !@connection || @connection.closed?
|
96
|
+
@connection = TCPSocket.open(@uri.host, @uri.port)
|
97
|
+
if @uri.scheme == 'https'
|
98
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
99
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
100
|
+
@connection = OpenSSL::SSL::SSLSocket.new(@connection, @ssl_context)
|
101
|
+
@connection.sync_close = true
|
102
|
+
@connection.connect
|
103
|
+
end
|
104
|
+
end
|
105
|
+
@connection
|
91
106
|
end
|
107
|
+
|
92
108
|
end
|
109
|
+
end
|
110
|
+
|
111
|
+
else
|
93
112
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
113
|
+
module Excon
|
114
|
+
class Connection
|
115
|
+
|
116
|
+
def initialize(url)
|
117
|
+
end
|
118
|
+
|
119
|
+
def request(params)
|
120
|
+
for key in Excon.mocks.keys
|
121
|
+
if key == params
|
122
|
+
response = Excon.mocks[key]
|
123
|
+
break
|
124
|
+
end
|
125
|
+
end
|
126
|
+
unless response
|
127
|
+
response = Excon::Response.new
|
128
|
+
response.status = 404
|
129
|
+
response.headers = { 'Content-Length' => 0 }
|
130
|
+
end
|
131
|
+
|
132
|
+
if params[:expects] && ![*params[:expects]].include?(response.status)
|
133
|
+
raise(Excon::Errors.status_error(params[:expects], response.status, response))
|
134
|
+
else
|
135
|
+
response
|
105
136
|
end
|
106
137
|
end
|
107
|
-
@connection
|
108
|
-
end
|
109
138
|
|
139
|
+
end
|
110
140
|
end
|
141
|
+
|
111
142
|
end
|
data/lib/excon/response.rb
CHANGED
@@ -3,9 +3,10 @@ module Excon
|
|
3
3
|
|
4
4
|
attr_accessor :body, :headers, :status
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
@body
|
8
|
-
@headers = {}
|
6
|
+
def initialize(attributes = {})
|
7
|
+
@body = attributes[:body] || ''
|
8
|
+
@headers = attributes[:headers] || {}
|
9
|
+
@status = attributes[:status]
|
9
10
|
end
|
10
11
|
|
11
12
|
end
|
data/test/test.rb
CHANGED
@@ -1,11 +1,26 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'lib/excon')
|
2
2
|
|
3
|
+
Excon.mock!
|
4
|
+
|
5
|
+
request = {
|
6
|
+
:host => 'www.google.com',
|
7
|
+
:method => 'GET',
|
8
|
+
:path => '/'
|
9
|
+
}
|
10
|
+
response = {
|
11
|
+
:status => 200,
|
12
|
+
:headers => { 'Content-Length' => '11' },
|
13
|
+
:body => 'Hello World'
|
14
|
+
}
|
15
|
+
Excon.mocks[request] = response
|
16
|
+
|
3
17
|
x = Excon.new('http://www.google.com')
|
4
18
|
|
5
19
|
10.times do
|
6
20
|
p x.request(
|
21
|
+
:host => 'www.google.com',
|
7
22
|
:method => 'GET',
|
8
|
-
:path
|
23
|
+
:path => '/'
|
9
24
|
)
|
10
25
|
end
|
11
26
|
|
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.3
|
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: 2009-
|
12
|
+
date: 2009-11-03 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|