excon 0.0.1
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/.document +5 -0
- data/.gitignore +5 -0
- data/README.rdoc +28 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/benchmarks/excon_vs.rb +39 -0
- data/benchmarks/headers_split_vs_match.rb +29 -0
- data/benchmarks/strip_newline.rb +69 -0
- data/lib/excon.rb +21 -0
- data/lib/excon/connection.rb +111 -0
- data/lib/excon/errors.rb +92 -0
- data/lib/excon/response.rb +12 -0
- data/test/test.rb +15 -0
- metadata +67 -0
data/.document
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
= excon
|
2
|
+
|
3
|
+
Http(s) EXtended CONnections
|
4
|
+
|
5
|
+
== Copyright
|
6
|
+
|
7
|
+
(The MIT License)
|
8
|
+
|
9
|
+
Copyright (c) 2009 {geemus (Wesley Beary)}[http://github.com/geemus]
|
10
|
+
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
12
|
+
a copy of this software and associated documentation files (the
|
13
|
+
"Software"), to deal in the Software without restriction, including
|
14
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
15
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
16
|
+
permit persons to whom the Software is furnished to do so, subject to
|
17
|
+
the following conditions:
|
18
|
+
|
19
|
+
The above copyright notice and this permission notice shall be
|
20
|
+
included in all copies or substantial portions of the Software.
|
21
|
+
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
23
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
24
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
25
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
26
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
27
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
28
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "excon"
|
8
|
+
gem.summary = %Q{EXtended http(s) CONnections}
|
9
|
+
gem.description = %Q{speed, persistence, http(s)}
|
10
|
+
gem.email = "wbeary@engineyard.com"
|
11
|
+
gem.homepage = "http://github.com/geemus/excon"
|
12
|
+
gem.authors = ["Wesley Beary"]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'rake/testtask'
|
20
|
+
Rake::TestTask.new(:test) do |test|
|
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
|
38
|
+
|
39
|
+
task :test => :check_dependencies
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rake/rdoctask'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
if File.exist?('VERSION')
|
46
|
+
version = File.read('VERSION')
|
47
|
+
else
|
48
|
+
version = ""
|
49
|
+
end
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "excon #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib/excon')
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
require 'net/http'
|
5
|
+
require 'open-uri'
|
6
|
+
|
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 => '/')
|
13
|
+
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 => '/')
|
19
|
+
end
|
20
|
+
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('/') }
|
25
|
+
end
|
26
|
+
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('/')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
bench.report('open-uri') do
|
35
|
+
COUNT.times do
|
36
|
+
open('http://www.google.com/').read
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
COUNT = 1_000_000
|
4
|
+
data = "Content-Length: 100"
|
5
|
+
Benchmark.bmbm(25) do |bench|
|
6
|
+
bench.report('regex') do
|
7
|
+
COUNT.times do
|
8
|
+
header = data.match(/(.*):\s(.*)/)
|
9
|
+
"#{header[1]}: #{header[2]}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
bench.report('split') do
|
13
|
+
COUNT.times do
|
14
|
+
header = data.split(': ')
|
15
|
+
"#{header[0]}: #{header[1]}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
# Rehearsal ------------------------------------------------------------
|
23
|
+
# regex 4.270000 0.010000 4.280000 ( 4.294186)
|
24
|
+
# split 3.870000 0.000000 3.870000 ( 3.885645)
|
25
|
+
# --------------------------------------------------- total: 8.150000sec
|
26
|
+
#
|
27
|
+
# user system total real
|
28
|
+
# regex 4.260000 0.010000 4.270000 ( 4.284764)
|
29
|
+
# split 3.860000 0.010000 3.870000 ( 3.882795)
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
COUNT = 1_000_000
|
4
|
+
data = "Content-Length: 100\r\n"
|
5
|
+
Benchmark.bmbm(25) do |bench|
|
6
|
+
bench.report('chomp') do
|
7
|
+
COUNT.times do
|
8
|
+
data = "Content-Length: 100\r\n"
|
9
|
+
data.chomp
|
10
|
+
end
|
11
|
+
end
|
12
|
+
bench.report('chomp!') do
|
13
|
+
COUNT.times do
|
14
|
+
data = "Content-Length: 100\r\n"
|
15
|
+
data.chomp!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
bench.report('chop') do
|
19
|
+
COUNT.times do
|
20
|
+
data = "Content-Length: 100\r\n"
|
21
|
+
data.chop
|
22
|
+
end
|
23
|
+
end
|
24
|
+
bench.report('chop!') do
|
25
|
+
COUNT.times do
|
26
|
+
data = "Content-Length: 100\r\n"
|
27
|
+
data.chop!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
bench.report('strip') do
|
31
|
+
COUNT.times do
|
32
|
+
data = "Content-Length: 100\r\n"
|
33
|
+
data.strip
|
34
|
+
end
|
35
|
+
end
|
36
|
+
bench.report('strip!') do
|
37
|
+
COUNT.times do
|
38
|
+
data = "Content-Length: 100\r\n"
|
39
|
+
data.strip!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
bench.report('index') do
|
43
|
+
COUNT.times do
|
44
|
+
data = "Content-Length: 100\r\n"
|
45
|
+
data[0..-3]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
# Rehearsal ------------------------------------------------------------
|
53
|
+
# chomp 0.640000 0.000000 0.640000 ( 0.644043)
|
54
|
+
# chomp! 0.530000 0.000000 0.530000 ( 0.531415)
|
55
|
+
# chop 0.620000 0.000000 0.620000 ( 0.624321)
|
56
|
+
# chop! 0.500000 0.000000 0.500000 ( 0.509146)
|
57
|
+
# strip 0.640000 0.000000 0.640000 ( 0.638785)
|
58
|
+
# strip! 0.530000 0.000000 0.530000 ( 0.532196)
|
59
|
+
# index 0.740000 0.000000 0.740000 ( 0.745742)
|
60
|
+
# --------------------------------------------------- total: 4.200000sec
|
61
|
+
#
|
62
|
+
# user system total real
|
63
|
+
# chomp 0.640000 0.010000 0.650000 ( 0.647287)
|
64
|
+
# chomp! 0.530000 0.000000 0.530000 ( 0.532868)
|
65
|
+
# chop 0.630000 0.000000 0.630000 ( 0.628236)
|
66
|
+
# chop! 0.520000 0.000000 0.520000 ( 0.522950)
|
67
|
+
# strip 0.640000 0.000000 0.640000 ( 0.646328)
|
68
|
+
# strip! 0.520000 0.000000 0.520000 ( 0.532715)
|
69
|
+
# index 0.740000 0.010000 0.750000 ( 0.771277)
|
data/lib/excon.rb
ADDED
@@ -0,0 +1,21 @@
|
|
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__))
|
6
|
+
|
7
|
+
require 'openssl'
|
8
|
+
require 'socket'
|
9
|
+
require 'uri'
|
10
|
+
|
11
|
+
require 'excon/connection'
|
12
|
+
require 'excon/errors'
|
13
|
+
require 'excon/response'
|
14
|
+
|
15
|
+
module Excon
|
16
|
+
|
17
|
+
def self.new(url)
|
18
|
+
Excon::Connection.new(url)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Excon
|
2
|
+
class Connection
|
3
|
+
|
4
|
+
unless defined?(:CHUNK_SIZE)
|
5
|
+
CHUNK_SIZE = 1048576 # 1 megabyte
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(url)
|
9
|
+
@uri = URI.parse(url)
|
10
|
+
end
|
11
|
+
|
12
|
+
def request(params)
|
13
|
+
begin
|
14
|
+
params[:path] ||= ''
|
15
|
+
unless params[:path][0..0] == '/'
|
16
|
+
params[:path] = "/#{params[:path]}"
|
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
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
response = Excon::Response.new
|
44
|
+
response.status = connection.readline[9..11].to_i
|
45
|
+
while true
|
46
|
+
data = connection.readline.chop!
|
47
|
+
unless data.empty?
|
48
|
+
header = data.split(': ')
|
49
|
+
response.headers[header[0]] = header[1]
|
50
|
+
else
|
51
|
+
break
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
unless params[:method] == 'HEAD'
|
56
|
+
unless params[:block]
|
57
|
+
response.body = ''
|
58
|
+
params[:block] = lambda { |chunk| response.body << chunk }
|
59
|
+
end
|
60
|
+
|
61
|
+
if response.headers['Content-Length']
|
62
|
+
remaining = response.headers['Content-Length'].to_i
|
63
|
+
while remaining > 0
|
64
|
+
params[:block].call(connection.read([CHUNK_SIZE, remaining].min))
|
65
|
+
remaining -= CHUNK_SIZE
|
66
|
+
end
|
67
|
+
elsif response.headers['Transfer-Encoding'] == 'chunked'
|
68
|
+
while true
|
69
|
+
chunk_size = connection.readline.chomp!.to_i(16)
|
70
|
+
chunk = connection.read(chunk_size + 2).chop! # 2 == "/r/n".length
|
71
|
+
if chunk_size > 0
|
72
|
+
params[:block].call(chunk)
|
73
|
+
else
|
74
|
+
break
|
75
|
+
end
|
76
|
+
end
|
77
|
+
elsif response.headers['Connection'] == 'close'
|
78
|
+
params[:block].call(connection.read)
|
79
|
+
@connection = nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
rescue => connection_error
|
83
|
+
@connection = nil
|
84
|
+
raise(connection_error)
|
85
|
+
end
|
86
|
+
|
87
|
+
if params[:expects] && ![*params[:expects]].include?(response.status)
|
88
|
+
raise(Excon::Errors.status_error(params[:expects], response.status, response))
|
89
|
+
else
|
90
|
+
response
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def connection
|
97
|
+
if !@connection || @connection.closed?
|
98
|
+
@connection = TCPSocket.open(@uri.host, @uri.port)
|
99
|
+
if @uri.scheme == 'https'
|
100
|
+
@ssl_context = OpenSSL::SSL::SSLContext.new
|
101
|
+
@ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
102
|
+
@connection = OpenSSL::SSL::SSLSocket.new(@connection, @ssl_context)
|
103
|
+
@connection.sync_close = true
|
104
|
+
@connection.connect
|
105
|
+
end
|
106
|
+
end
|
107
|
+
@connection
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
data/lib/excon/errors.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
module Excon
|
2
|
+
|
3
|
+
module Errors
|
4
|
+
class Continue < StandardError; end # 100
|
5
|
+
class SwitchingProtocols < StandardError; end # 101
|
6
|
+
class OK < StandardError; end # 200
|
7
|
+
class Created < StandardError; end # 201
|
8
|
+
class Accepted < StandardError; end # 202
|
9
|
+
class NonAuthoritativeInformation < StandardError; end # 203
|
10
|
+
class NoContent < StandardError; end # 204
|
11
|
+
class ResetContent < StandardError; end # 205
|
12
|
+
class PartialContent < StandardError; end # 206
|
13
|
+
class MultipleChoices < StandardError; end # 300
|
14
|
+
class MovedPermanently < StandardError; end # 301
|
15
|
+
class Found < StandardError; end # 302
|
16
|
+
class SeeOther < StandardError; end # 303
|
17
|
+
class NotModified < StandardError; end # 304
|
18
|
+
class UseProxy < StandardError; end # 305
|
19
|
+
class TemporaryRedirect < StandardError; end # 307
|
20
|
+
class BadRequest < StandardError; end # 400
|
21
|
+
class Unauthorized < StandardError; end # 401
|
22
|
+
class PaymentRequired < StandardError; end # 402
|
23
|
+
class Forbidden < StandardError; end # 403
|
24
|
+
class NotFound < StandardError; end # 404
|
25
|
+
class MethodNotAllowed < StandardError; end # 405
|
26
|
+
class NotAcceptable < StandardError; end # 406
|
27
|
+
class ProxyAuthenticationRequired < StandardError; end # 407
|
28
|
+
class RequestTimeout < StandardError; end # 408
|
29
|
+
class Conflict < StandardError; end # 409
|
30
|
+
class Gone < StandardError; end # 410
|
31
|
+
class LengthRequired < StandardError; end # 411
|
32
|
+
class PreconditionFailed < StandardError; end # 412
|
33
|
+
class RequestEntityTooLarge < StandardError; end # 412
|
34
|
+
class RequestURITooLong < StandardError; end # 414
|
35
|
+
class UnsupportedMediaType < StandardError; end # 415
|
36
|
+
class RequestedRangeNotSatisfiable < StandardError; end # 416
|
37
|
+
class ExpectationFailed < StandardError; end # 417
|
38
|
+
class InternalServerError < StandardError; end # 500
|
39
|
+
class NotImplemented < StandardError; end # 501
|
40
|
+
class BadGateway < StandardError; end # 502
|
41
|
+
class ServiceUnavailable < StandardError; end # 503
|
42
|
+
class GatewayTimeout < StandardError; end # 504
|
43
|
+
|
44
|
+
# Messages for nicer exceptions, from rfc2616
|
45
|
+
def self.status_error(expected, actual, response)
|
46
|
+
@errors ||= {
|
47
|
+
100 => [Excon::Errors::Continue, 'Continue'],
|
48
|
+
101 => [Excon::Errors::SwitchingProtocols, 'Switching Protocols'],
|
49
|
+
200 => [Excon::Errors::OK, 'OK'],
|
50
|
+
201 => [Excon::Errors::Created, 'Created'],
|
51
|
+
202 => [Excon::Errors::Accepted, 'Accepted'],
|
52
|
+
203 => [Excon::Errors::NonAuthoritativeInformation, 'Non-Authoritative Information'],
|
53
|
+
204 => [Excon::Errors::NoContent, 'No Content'],
|
54
|
+
205 => [Excon::Errors::ResetContent, 'Reset Content'],
|
55
|
+
206 => [Excon::Errors::PartialContent, 'Partial Content'],
|
56
|
+
300 => [Excon::Errors::MultipleChoices, 'Multiple Choices'],
|
57
|
+
301 => [Excon::Errors::MovedPermanently, 'Moved Permanently'],
|
58
|
+
302 => [Excon::Errors::Found, 'Found'],
|
59
|
+
303 => [Excon::Errors::SeeOther, 'See Other'],
|
60
|
+
304 => [Excon::Errors::NotModified, 'Not Modified'],
|
61
|
+
305 => [Excon::Errors::UseProxy, 'Use Proxy'],
|
62
|
+
307 => [Excon::Errors::TemporaryRedirect, 'Temporary Redirect'],
|
63
|
+
400 => [Excon::Errors::BadRequest, 'Bad Request'],
|
64
|
+
401 => [Excon::Errors::Unauthorized, 'Unauthorized'],
|
65
|
+
402 => [Excon::Errors::PaymentRequired, 'Payment Required'],
|
66
|
+
403 => [Excon::Errors::Forbidden, 'Forbidden'],
|
67
|
+
404 => [Excon::Errors::NotFound, 'Not Found'],
|
68
|
+
405 => [Excon::Errors::MethodNotAllowed, 'Method Not Allowed'],
|
69
|
+
406 => [Excon::Errors::NotAcceptable, 'Not Acceptable'],
|
70
|
+
407 => [Excon::Errors::ProxyAuthenticationRequired, 'Proxy Authentication Required'],
|
71
|
+
408 => [Excon::Errors::RequestTimeout, 'Request Timeout'],
|
72
|
+
409 => [Excon::Errors::Conflict, 'Conflict'],
|
73
|
+
410 => [Excon::Errors::Gone, 'Gone'],
|
74
|
+
411 => [Excon::Errors::LengthRequired, 'Length Required'],
|
75
|
+
412 => [Excon::Errors::PreconditionFailed, 'Precondition Failed'],
|
76
|
+
413 => [Excon::Errors::RequestEntityTooLarge, 'Request Entity Too Large'],
|
77
|
+
414 => [Excon::Errors::RequestURITooLong, 'Request-URI Too Long'],
|
78
|
+
415 => [Excon::Errors::UnsupportedMediaType, 'Unsupported Media Type'],
|
79
|
+
416 => [Excon::Errors::RequestedRangeNotSatisfiable, 'Request Range Not Satisfiable'],
|
80
|
+
417 => [Excon::Errors::ExpectationFailed, 'Expectation Failed'],
|
81
|
+
500 => [Excon::Errors::InternalServerError, 'InternalServerError'],
|
82
|
+
501 => [Excon::Errors::NotImplemented, 'Not Implemented'],
|
83
|
+
502 => [Excon::Errors::BadGateway, 'Bad Gateway'],
|
84
|
+
503 => [Excon::Errors::ServiceUnavailable, 'Service Unavailable'],
|
85
|
+
504 => [Excon::Errors::GatewayTimeout, 'Gateway Timeout']
|
86
|
+
}
|
87
|
+
error = @errors[actual]
|
88
|
+
error[0].new("Expected(#{expected.inspect}) <=> Actual(#{actual} #{error[1]}): #{response.body}")
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
data/test/test.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib/excon')
|
2
|
+
|
3
|
+
x = Excon.new('http://www.google.com')
|
4
|
+
|
5
|
+
10.times do
|
6
|
+
p x.request(
|
7
|
+
:method => 'GET',
|
8
|
+
:path => '/'
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
# require 'open-uri'
|
13
|
+
# 10.times do
|
14
|
+
# p open('http://www.google.com').read
|
15
|
+
# end
|
metadata
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: excon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wesley Beary
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-31 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: speed, persistence, http(s)
|
17
|
+
email: wbeary@engineyard.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- .document
|
26
|
+
- .gitignore
|
27
|
+
- README.rdoc
|
28
|
+
- Rakefile
|
29
|
+
- VERSION
|
30
|
+
- benchmarks/excon_vs.rb
|
31
|
+
- benchmarks/headers_split_vs_match.rb
|
32
|
+
- benchmarks/strip_newline.rb
|
33
|
+
- lib/excon.rb
|
34
|
+
- lib/excon/connection.rb
|
35
|
+
- lib/excon/errors.rb
|
36
|
+
- lib/excon/response.rb
|
37
|
+
- test/test.rb
|
38
|
+
has_rdoc: true
|
39
|
+
homepage: http://github.com/geemus/excon
|
40
|
+
licenses: []
|
41
|
+
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options:
|
44
|
+
- --charset=UTF-8
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.3.5
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: EXtended http(s) CONnections
|
66
|
+
test_files:
|
67
|
+
- test/test.rb
|