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 CHANGED
@@ -1,11 +1,13 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- group :test do
4
- gem "shindo"
5
- gem "tach"
3
+ gemspec
6
4
 
7
- gem "sinatra"
8
- gem "rake"
9
- gem "mongrel"
10
- gem "open4"
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
@@ -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' << 'test'
51
- test.pattern = 'test/**/test_*.rb'
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 test/test_*.rb"
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
+ # +-------------+----------+
@@ -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
+ # +----------+----------+
@@ -1,39 +1,138 @@
1
- require File.join(File.dirname(__FILE__), '..', 'lib/excon')
1
+ require 'rubygems' if RUBY_VERSION < '1.9'
2
+ require 'bundler'
2
3
 
3
- require 'benchmark'
4
- require 'net/http'
5
- require 'open-uri'
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
- COUNT = 100
8
- data = "Content-Length: 100"
9
- Benchmark.bmbm(25) do |bench|
10
- bench.report('excon') do
11
- COUNT.times do
12
- Excon.new('http://www.google.com').request(:method => 'GET', :path => '/')
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
- end
15
- bench.report('excon (persistent)') do
16
- excon = Excon.new('http://www.google.com')
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
- bench.report('net/http') do
22
- COUNT.times do
23
- # Net::HTTP.get('www.google.com', '/')
24
- Net::HTTP.start('www.google.com') {|http| http.get('/') }
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
- bench.report('net/http (persistent)') do
28
- Net::HTTP.start('www.google.com', 80) do |http|
29
- COUNT.times do
30
- http.get('/')
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
- end
34
- bench.report('open-uri') do
35
- COUNT.times do
36
- open('http://www.google.com/').read
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
+ # +--------------------------+----------+
@@ -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.4'
17
- s.date = '2010-10-11'
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
@@ -1,8 +1,5 @@
1
- __DIR__ = File.dirname(__FILE__)
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.4'
16
+ VERSION = '0.2.6'
20
17
  end
21
18
 
22
- CHUNK_SIZE = 1048576 # 1 megabyte
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 => '#{method.upcase}'), &block)
36
+ new(url).request(params.merge!(:method => :#{method}), &block)
32
37
  end
33
38
  DEF
34
39
  end
@@ -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] = "/#{params[:path]}"
47
+ params[:path].insert(0, '/')
21
48
  end
22
- request = "#{params[:method].upcase} #{params[:path]}?"
23
- for key, values in (params[:query] || @connection[:query] || {})
24
- for value in [*values]
25
- request << "#{key}#{value && "=#{CGI.escape(value.to_s)}"}&"
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.chop!
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 << "#{key}: #{value}\r\n"
83
+ request << key.to_s << ': ' << value.to_s << CR_NL
47
84
  end
48
- request << "\r\n"
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
- "#{@connection[:host]}:#{@connection[:port]}"
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
@@ -1,6 +1,7 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib/excon'))
1
+ require 'rubygems' if RUBY_VERSION < '1.9'
2
+ require 'bundler'
2
3
 
3
- require 'open4'
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
- - 4
9
- version: 0.2.4
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-10-11 00:00:00 -07:00
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