excon 0.2.4 → 0.2.6
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/Gemfile +9 -7
- data/README.rdoc +1 -1
- data/Rakefile +3 -3
- data/benchmarks/concat_vs_insert.rb +21 -0
- data/benchmarks/concat_vs_interpolate.rb +21 -0
- data/benchmarks/cr_lf.rb +21 -0
- data/benchmarks/excon_vs.rb +126 -27
- data/excon.gemspec +8 -2
- data/lib/excon.rb +13 -8
- data/lib/excon/connection.rb +51 -10
- data/tests/test_helper.rb +3 -2
- metadata +45 -5
data/Gemfile
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
|
4
|
-
gem "shindo"
|
5
|
-
gem "tach"
|
3
|
+
gemspec
|
6
4
|
|
7
|
-
|
8
|
-
gem
|
9
|
-
gem
|
10
|
-
gem
|
5
|
+
group :benchmark do
|
6
|
+
gem 'em-http-request'
|
7
|
+
gem 'httparty'
|
8
|
+
gem 'rest-client'
|
9
|
+
gem 'tach', '0.0.5'
|
10
|
+
gem 'typhoeus'
|
11
|
+
gem 'sinatra'
|
12
|
+
gem 'streamly_ffi'
|
11
13
|
end
|
data/README.rdoc
CHANGED
@@ -14,7 +14,7 @@ This will return a response object, which has body, headers and status attribute
|
|
14
14
|
|
15
15
|
This supports all the major http verbs: [connect, delete, get, head, options, post, put, trace]
|
16
16
|
|
17
|
-
You can also create a connection to try and keep open across multiple requests.
|
17
|
+
You can also create a connection to try and keep open across multiple requests (more performant!).
|
18
18
|
|
19
19
|
connection = Excon.new('http://geemus.com')
|
20
20
|
connection.request(:method => 'GET')
|
data/Rakefile
CHANGED
@@ -47,8 +47,8 @@ task :default => :test
|
|
47
47
|
|
48
48
|
require 'rake/testtask'
|
49
49
|
Rake::TestTask.new(:test) do |test|
|
50
|
-
test.libs << 'lib' << '
|
51
|
-
test.pattern = '
|
50
|
+
test.libs << 'lib' << 'tests'
|
51
|
+
test.pattern = 'tests/**/test_*.rb'
|
52
52
|
test.verbose = true
|
53
53
|
end
|
54
54
|
|
@@ -56,7 +56,7 @@ desc "Generate RCov test coverage and open in your browser"
|
|
56
56
|
task :coverage do
|
57
57
|
require 'rcov'
|
58
58
|
sh "rm -fr coverage"
|
59
|
-
sh "rcov
|
59
|
+
sh "rcov tests/test_*.rb"
|
60
60
|
sh "open coverage/index.html"
|
61
61
|
end
|
62
62
|
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tach'
|
3
|
+
|
4
|
+
Tach.meter(1_000_000) do
|
5
|
+
tach('concat') do
|
6
|
+
path = 'path'
|
7
|
+
path = '/' << path
|
8
|
+
end
|
9
|
+
tach('insert') do
|
10
|
+
path = 'path'
|
11
|
+
path.insert(0, '/')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# +--------+----------+
|
16
|
+
# | tach | total |
|
17
|
+
# +--------+----------+
|
18
|
+
# | insert | 0.974036 |
|
19
|
+
# +--------+----------+
|
20
|
+
# | concat | 0.998904 |
|
21
|
+
# +--------+----------+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tach'
|
3
|
+
|
4
|
+
key = 'Content-Length'
|
5
|
+
value = '100'
|
6
|
+
Tach.meter(1_000) do
|
7
|
+
tach('concat') do
|
8
|
+
key << ': ' << value << "\r\n"
|
9
|
+
end
|
10
|
+
tach('interpolate') do
|
11
|
+
"#{key}: value\r\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# +-------------+----------+
|
16
|
+
# | tach | total |
|
17
|
+
# +-------------+----------+
|
18
|
+
# | concat | 0.000902 |
|
19
|
+
# +-------------+----------+
|
20
|
+
# | interpolate | 0.019667 |
|
21
|
+
# +-------------+----------+
|
data/benchmarks/cr_lf.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tach'
|
3
|
+
|
4
|
+
CR_LF = "\r\n"
|
5
|
+
|
6
|
+
Tach.meter(1_000_000) do
|
7
|
+
tach('constant') do
|
8
|
+
'' << CR_LF
|
9
|
+
end
|
10
|
+
tach('string') do
|
11
|
+
'' << "\r\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# +----------+----------+
|
16
|
+
# | tach | total |
|
17
|
+
# +----------+----------+
|
18
|
+
# | constant | 0.819885 |
|
19
|
+
# +----------+----------+
|
20
|
+
# | string | 0.893602 |
|
21
|
+
# +----------+----------+
|
data/benchmarks/excon_vs.rb
CHANGED
@@ -1,39 +1,138 @@
|
|
1
|
-
require
|
1
|
+
require 'rubygems' if RUBY_VERSION < '1.9'
|
2
|
+
require 'bundler'
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
|
4
|
+
Bundler.require(:default)
|
5
|
+
Bundler.require(:benchmark)
|
6
|
+
|
7
|
+
require 'sinatra/base'
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'excon')
|
10
|
+
|
11
|
+
module Excon
|
12
|
+
class Server < Sinatra::Base
|
6
13
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
def self.run
|
15
|
+
Rack::Handler::WEBrick.run(
|
16
|
+
Excon::Server.new,
|
17
|
+
:Port => 9292,
|
18
|
+
:AccessLog => [],
|
19
|
+
:Logger => WEBrick::Log.new(nil, WEBrick::Log::ERROR)
|
20
|
+
)
|
13
21
|
end
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
COUNT.times do
|
18
|
-
excon.request(:method => 'GET', :path => '/')
|
22
|
+
|
23
|
+
get '/data/:amount' do |amount|
|
24
|
+
'x' * amount.to_i
|
19
25
|
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def with_server(&block)
|
31
|
+
pid = Process.fork do
|
32
|
+
Excon::Server.run
|
20
33
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
loop do
|
35
|
+
sleep(1)
|
36
|
+
begin
|
37
|
+
Excon.get('http://localhost:9292/api/foo')
|
38
|
+
break
|
39
|
+
rescue
|
25
40
|
end
|
26
41
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
42
|
+
yield
|
43
|
+
ensure
|
44
|
+
Process.kill(9, pid)
|
45
|
+
end
|
46
|
+
|
47
|
+
require 'em-http-request'
|
48
|
+
require 'httparty'
|
49
|
+
require 'net/http'
|
50
|
+
require 'open-uri'
|
51
|
+
require 'rest_client'
|
52
|
+
require 'tach'
|
53
|
+
require 'typhoeus'
|
54
|
+
|
55
|
+
url = 'http://localhost:9292/data/1000'
|
56
|
+
|
57
|
+
with_server do
|
58
|
+
|
59
|
+
Tach.meter(1000) do
|
60
|
+
|
61
|
+
tach('em-http-request') do
|
62
|
+
EventMachine.run {
|
63
|
+
http = EventMachine::HttpRequest.new(url).get
|
64
|
+
|
65
|
+
http.callback {
|
66
|
+
http.response
|
67
|
+
EventMachine.stop
|
68
|
+
}
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
tach('Excon') do
|
73
|
+
Excon.get(url).body
|
74
|
+
end
|
75
|
+
|
76
|
+
excon = Excon.new(url)
|
77
|
+
tach('Excon (persistent)') do
|
78
|
+
excon.request(:method => 'get').body
|
79
|
+
end
|
80
|
+
|
81
|
+
tach('HTTParty') do
|
82
|
+
HTTParty.get(url).body
|
83
|
+
end
|
84
|
+
|
85
|
+
tach('Net::HTTP') do
|
86
|
+
# Net::HTTP.get('localhost', '/data/1000', 9292)
|
87
|
+
Net::HTTP.start('localhost', 9292) {|http| http.get('/data/1000').body }
|
88
|
+
end
|
89
|
+
|
90
|
+
Net::HTTP.start('localhost', 9292) do |http|
|
91
|
+
tach('Net::HTTP (persistent)') do
|
92
|
+
http.get('/data/1000').body
|
31
93
|
end
|
32
94
|
end
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
95
|
+
|
96
|
+
tach('open-uri') do
|
97
|
+
open(url).read
|
98
|
+
end
|
99
|
+
|
100
|
+
tach('RestClient') do
|
101
|
+
RestClient.get(url)
|
102
|
+
end
|
103
|
+
|
104
|
+
streamly = StreamlyFFI::Connection.new
|
105
|
+
tach('StreamlyFFI (persistent)') do
|
106
|
+
streamly.get(url)
|
107
|
+
end
|
108
|
+
|
109
|
+
tach('Typhoeus') do
|
110
|
+
Typhoeus::Request.get(url).body
|
37
111
|
end
|
112
|
+
|
38
113
|
end
|
39
114
|
end
|
115
|
+
|
116
|
+
# +--------------------------+----------+
|
117
|
+
# | tach | total |
|
118
|
+
# +--------------------------+----------+
|
119
|
+
# | Excon (persistent) | 1.521565 |
|
120
|
+
# +--------------------------+----------+
|
121
|
+
# | Excon | 1.624180 |
|
122
|
+
# +--------------------------+----------+
|
123
|
+
# | Typhoeus | 1.918563 |
|
124
|
+
# +--------------------------+----------+
|
125
|
+
# | StreamlyFFI (persistent) | 1.933218 |
|
126
|
+
# +--------------------------+----------+
|
127
|
+
# | Net::HTTP (persistent) | 2.418454 |
|
128
|
+
# +--------------------------+----------+
|
129
|
+
# | Net::HTTP | 2.439801 |
|
130
|
+
# +--------------------------+----------+
|
131
|
+
# | HTTParty | 2.609180 |
|
132
|
+
# +--------------------------+----------+
|
133
|
+
# | RestClient | 2.905853 |
|
134
|
+
# +--------------------------+----------+
|
135
|
+
# | open-uri | 2.936382 |
|
136
|
+
# +--------------------------+----------+
|
137
|
+
# | em-http-request | 3.933757 |
|
138
|
+
# +--------------------------+----------+
|
data/excon.gemspec
CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
|
|
13
13
|
## If your rubyforge_project name is different, then edit it and comment out
|
14
14
|
## the sub! line in the Rakefile
|
15
15
|
s.name = 'excon'
|
16
|
-
s.version = '0.2.
|
17
|
-
s.date = '2010-
|
16
|
+
s.version = '0.2.6'
|
17
|
+
s.date = '2010-11-29'
|
18
18
|
s.rubyforge_project = 'excon'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -53,6 +53,9 @@ Gem::Specification.new do |s|
|
|
53
53
|
## List your development dependencies here. Development dependencies are
|
54
54
|
## those that are only needed during development
|
55
55
|
# s.add_development_dependency('DEVDEPNAME', [">= 1.1.0", "< 2.0.0"])
|
56
|
+
s.add_development_dependency('open4')
|
57
|
+
s.add_development_dependency('shindo', '0.1.10')
|
58
|
+
s.add_development_dependency('sinatra')
|
56
59
|
|
57
60
|
## Leave this section as-is. It will be automatically generated from the
|
58
61
|
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
@@ -62,6 +65,9 @@ Gem::Specification.new do |s|
|
|
62
65
|
Gemfile
|
63
66
|
README.rdoc
|
64
67
|
Rakefile
|
68
|
+
benchmarks/concat_vs_insert.rb
|
69
|
+
benchmarks/concat_vs_interpolate.rb
|
70
|
+
benchmarks/cr_lf.rb
|
65
71
|
benchmarks/excon_vs.rb
|
66
72
|
benchmarks/headers_split_vs_match.rb
|
67
73
|
benchmarks/strip_newline.rb
|
data/lib/excon.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift __DIR__ unless
|
4
|
-
$LOAD_PATH.include?(__DIR__) ||
|
5
|
-
$LOAD_PATH.include?(File.expand_path(__DIR__))
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
6
3
|
|
7
4
|
require 'cgi'
|
8
5
|
require 'openssl'
|
@@ -16,11 +13,19 @@ require 'excon/response'
|
|
16
13
|
module Excon
|
17
14
|
|
18
15
|
unless const_defined?(:VERSION)
|
19
|
-
VERSION = '0.2.
|
16
|
+
VERSION = '0.2.6'
|
20
17
|
end
|
21
18
|
|
22
|
-
CHUNK_SIZE
|
19
|
+
unless const_defined?(:CHUNK_SIZE)
|
20
|
+
CHUNK_SIZE = 1048576 # 1 megabyte
|
21
|
+
end
|
23
22
|
|
23
|
+
# @see Connection#initialize
|
24
|
+
# Initializes a new keep-alive session for a given remote host
|
25
|
+
#
|
26
|
+
# @param [String] url The destination URL
|
27
|
+
# @param [Hash<Symbol, >] params One or more option params to set on the Connection instance
|
28
|
+
# @return [Connection] A new Excon::Connection instance
|
24
29
|
def self.new(url, params = {})
|
25
30
|
Excon::Connection.new(url, params)
|
26
31
|
end
|
@@ -28,7 +33,7 @@ module Excon
|
|
28
33
|
%w{connect delete get head options post put trace}.each do |method|
|
29
34
|
eval <<-DEF
|
30
35
|
def self.#{method}(url, params = {}, &block)
|
31
|
-
new(url).request(params.merge!(:method =>
|
36
|
+
new(url).request(params.merge!(:method => :#{method}), &block)
|
32
37
|
end
|
33
38
|
DEF
|
34
39
|
end
|
data/lib/excon/connection.rb
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
module Excon
|
2
2
|
class Connection
|
3
3
|
|
4
|
+
attr_reader :connection
|
5
|
+
|
6
|
+
CR_NL = "\r\n"
|
7
|
+
HTTP_1_1 = " HTTP/1.1\r\n"
|
8
|
+
|
9
|
+
# Initializes a new Connection instance
|
10
|
+
# @param [String] url The destination URL
|
11
|
+
# @param [Hash<Symbol, >] params One or more optional params
|
12
|
+
# @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
|
13
|
+
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request
|
14
|
+
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
|
15
|
+
# @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
|
16
|
+
# @option params [Fixnum] :port The port on which to connect, to the destination host
|
17
|
+
# @option params [Hash] :query Default query; appended to the 'scheme://host:port/path/' in the form of '?key=value'. Will only be used if params[:query] is not supplied to Connection#request
|
18
|
+
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
|
4
19
|
def initialize(url, params = {})
|
5
20
|
uri = URI.parse(url)
|
6
21
|
@connection = {
|
@@ -11,22 +26,44 @@ module Excon
|
|
11
26
|
:query => uri.query,
|
12
27
|
:scheme => uri.scheme
|
13
28
|
}.merge!(params)
|
29
|
+
|
30
|
+
set_socket_key
|
14
31
|
end
|
15
32
|
|
33
|
+
# Sends the supplied request to the destination host.
|
34
|
+
# @yield [chunk] @see Response#self.parse
|
35
|
+
# @param [Hash<Symbol, >] params One or more optional params, override defaults set in Connection.new
|
36
|
+
# @option params [String] :body text to be sent over a socket
|
37
|
+
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request
|
38
|
+
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
|
39
|
+
# @option params [String] :path appears after 'scheme://host:port/'
|
40
|
+
# @option params [Fixnum] :port The port on which to connect, to the destination host
|
41
|
+
# @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
|
42
|
+
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
|
16
43
|
def request(params, &block)
|
17
44
|
begin
|
18
45
|
params[:path] ||= @connection[:path]
|
19
46
|
unless params[:path][0..0] == '/'
|
20
|
-
params[:path]
|
47
|
+
params[:path].insert(0, '/')
|
21
48
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
49
|
+
|
50
|
+
request = params[:method].to_s.upcase << ' ' << params[:path]
|
51
|
+
if query = (params[:query] || @connection[:query])
|
52
|
+
request << '?'
|
53
|
+
case query
|
54
|
+
when String
|
55
|
+
request << query
|
56
|
+
else
|
57
|
+
for key, values in query
|
58
|
+
for value in [*values]
|
59
|
+
value_string = value && ('=' << CGI.escape(value.to_s))
|
60
|
+
request << key.to_s << value_string << '&'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
request.chop! # remove trailing '&'
|
26
64
|
end
|
27
65
|
end
|
28
|
-
request
|
29
|
-
request << " HTTP/1.1\r\n"
|
66
|
+
request << HTTP_1_1
|
30
67
|
params[:headers] ||= @connection[:headers]
|
31
68
|
params[:headers]['Host'] ||= params[:host] || @connection[:host]
|
32
69
|
params[:body] ||= @connection[:body]
|
@@ -43,9 +80,9 @@ module Excon
|
|
43
80
|
0
|
44
81
|
end
|
45
82
|
for key, value in params[:headers]
|
46
|
-
request <<
|
83
|
+
request << key.to_s << ': ' << value.to_s << CR_NL
|
47
84
|
end
|
48
|
-
request <<
|
85
|
+
request << CR_NL
|
49
86
|
socket.write(request)
|
50
87
|
|
51
88
|
if params[:body]
|
@@ -126,7 +163,11 @@ module Excon
|
|
126
163
|
end
|
127
164
|
|
128
165
|
def socket_key
|
129
|
-
|
166
|
+
@connection[:socket_key]
|
167
|
+
end
|
168
|
+
|
169
|
+
def set_socket_key
|
170
|
+
@connection[:socket_key] = "#{@connection[:host]}:#{@connection[:port]}"
|
130
171
|
end
|
131
172
|
end
|
132
173
|
end
|
data/tests/test_helper.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
require
|
1
|
+
require 'rubygems' if RUBY_VERSION < '1.9'
|
2
|
+
require 'bundler'
|
2
3
|
|
3
|
-
require
|
4
|
+
Bundler.require(:default, :development)
|
4
5
|
|
5
6
|
def local_file(*parts)
|
6
7
|
File.expand_path(File.join(File.dirname(__FILE__), *parts))
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 6
|
9
|
+
version: 0.2.6
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- geemus (Wesley Beary)
|
@@ -14,10 +14,47 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-11-29 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: open4
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :development
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: shindo
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
- 1
|
42
|
+
- 10
|
43
|
+
version: 0.1.10
|
44
|
+
type: :development
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: sinatra
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
type: :development
|
57
|
+
version_requirements: *id003
|
21
58
|
description: EXtended http(s) CONnections
|
22
59
|
email: geemus@gmail.com
|
23
60
|
executables: []
|
@@ -30,6 +67,9 @@ files:
|
|
30
67
|
- Gemfile
|
31
68
|
- README.rdoc
|
32
69
|
- Rakefile
|
70
|
+
- benchmarks/concat_vs_insert.rb
|
71
|
+
- benchmarks/concat_vs_interpolate.rb
|
72
|
+
- benchmarks/cr_lf.rb
|
33
73
|
- benchmarks/excon_vs.rb
|
34
74
|
- benchmarks/headers_split_vs_match.rb
|
35
75
|
- benchmarks/strip_newline.rb
|