http-request 1.1.6 → 1.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/http-request.rb +23 -0
- data/lib/http-request/client.rb +32 -4
- data/lib/http-request/connection.rb +27 -35
- data/lib/http-request/version.rb +1 -1
- metadata +5 -6
- data/lib/old.rb +0 -198
data/lib/http-request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'net/http'
|
2
|
+
require 'net/https'
|
2
3
|
require 'addressable/uri'
|
3
4
|
|
4
5
|
module HttpRequest
|
@@ -13,6 +14,28 @@ module HttpRequest
|
|
13
14
|
client.open(url)
|
14
15
|
client
|
15
16
|
end
|
17
|
+
|
18
|
+
def config(options={})
|
19
|
+
# Connection defaults
|
20
|
+
@config ||= {
|
21
|
+
:user_agent => "HttpRequest v#{HttpRequest::VERSION}",
|
22
|
+
:max_retries => 3,
|
23
|
+
:timeout => 30, # default seconds to timeout a request
|
24
|
+
:auto_redirect => true,
|
25
|
+
:max_redirects => 3,
|
26
|
+
:keep_alive => true
|
27
|
+
}
|
28
|
+
@config.tap {|config| config.merge(options) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Short-hand methods, auto-create a client object
|
32
|
+
[:get, :post, :put, :delete, :head].each do |verb|
|
33
|
+
define_method(verb) do |*args|
|
34
|
+
url, headers, body = args
|
35
|
+
@client ||= open(url)
|
36
|
+
@client.send verb, *args
|
37
|
+
end
|
38
|
+
end
|
16
39
|
end
|
17
40
|
|
18
41
|
require 'http-request/version'
|
data/lib/http-request/client.rb
CHANGED
@@ -13,7 +13,7 @@ module HttpRequest
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def initialize(options={})
|
16
|
-
@options = options # Connection options
|
16
|
+
@options = HttpRequest.config.merge(options) # Connection options
|
17
17
|
@connections = []
|
18
18
|
end
|
19
19
|
|
@@ -47,14 +47,38 @@ module HttpRequest
|
|
47
47
|
[:get, :post, :put, :delete, :head].each do |verb|
|
48
48
|
define_method(verb) do |*args|
|
49
49
|
url, headers, body = args
|
50
|
-
url ||= open_url
|
51
50
|
request(verb, url, headers || {}, body)
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
55
54
|
def request(method, url, headers={}, body=nil)
|
55
|
+
url ||= open_url
|
56
|
+
# TODO: raise an exception if the url is invalid...
|
57
|
+
# ie. nil or something else...
|
56
58
|
conn = open(url)
|
57
|
-
conn.send
|
59
|
+
raw_response = conn.send(method, url, headers, body)
|
60
|
+
|
61
|
+
response = Response.new(raw_response, url)
|
62
|
+
|
63
|
+
# TODO: .. max retries.. keeping track of state..
|
64
|
+
# is can create a bunch of connections in this process...
|
65
|
+
# but how do we manage that plus the idea of threads..?
|
66
|
+
# each thread has their own Client object.. which is a good thing..
|
67
|
+
|
68
|
+
if options[:auto_redirect] != false && raw_response.kind_of?(Net::HTTPRedirection) && !response.header['location'].nil?
|
69
|
+
# TODO: .. count max redirects...
|
70
|
+
new_location = response.header['location']
|
71
|
+
|
72
|
+
if new_location =~ /^http/i
|
73
|
+
request(method, new_location, headers, body)
|
74
|
+
else
|
75
|
+
absolute_url = uri(url).join(new_location).to_s
|
76
|
+
request(method, absolute_url, headers, body)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
# reset redirect_count ..
|
80
|
+
response
|
81
|
+
end
|
58
82
|
end
|
59
83
|
|
60
84
|
def connection(url)
|
@@ -62,7 +86,11 @@ module HttpRequest
|
|
62
86
|
end
|
63
87
|
|
64
88
|
def server_id(url)
|
65
|
-
|
89
|
+
uri(url).normalized_site
|
90
|
+
end
|
91
|
+
|
92
|
+
def uri(url)
|
93
|
+
Addressable::URI.parse(url)
|
66
94
|
end
|
67
95
|
|
68
96
|
private
|
@@ -1,15 +1,16 @@
|
|
1
1
|
module HttpRequest
|
2
2
|
class Connection
|
3
3
|
attr_reader :uri, :http
|
4
|
-
attr_accessor :user_agent, :max_retries, :timeout, :auto_redirect, :keep_alive
|
4
|
+
attr_accessor :user_agent, :max_retries, :timeout, :auto_redirect, :max_redirects, :keep_alive
|
5
5
|
|
6
6
|
def initialize(url, options={})
|
7
|
-
#
|
8
|
-
@user_agent = options[:user_agent]
|
9
|
-
@max_retries = options[:max_retries]
|
10
|
-
@timeout = options[:timeout]
|
11
|
-
@auto_redirect = options[:auto_redirect]
|
12
|
-
@
|
7
|
+
# Connection settings
|
8
|
+
@user_agent = options[:user_agent] || HttpRequest.config[:user_agent]
|
9
|
+
@max_retries = options[:max_retries] || HttpRequest.config[:max_retries]
|
10
|
+
@timeout = options[:timeout] || HttpRequest.config[:timeout]
|
11
|
+
@auto_redirect = options[:auto_redirect] || HttpRequest.config[:auto_redirect]
|
12
|
+
@max_redirects = options[:max_redirects] || HttpRequest.config[:max_redirects]
|
13
|
+
@keep_alive = options[:keep_alive] || HttpRequest.config[:keep_alive]
|
13
14
|
|
14
15
|
# Let's do this
|
15
16
|
@uri = parse_uri(url)
|
@@ -17,9 +18,17 @@ module HttpRequest
|
|
17
18
|
end
|
18
19
|
|
19
20
|
def open
|
21
|
+
uri.port ||= 443 if ssl?
|
22
|
+
|
20
23
|
@http = Net::HTTP.new(uri.host, uri.port)
|
21
24
|
http.open_timeout = timeout
|
22
25
|
http.read_timeout = timeout
|
26
|
+
|
27
|
+
if ssl?
|
28
|
+
http.use_ssl = true
|
29
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
30
|
+
end
|
31
|
+
|
23
32
|
http.start
|
24
33
|
|
25
34
|
if socket && socket.respond_to?(:io) && Socket.const_defined?(:TCP_NODELAY)
|
@@ -27,6 +36,10 @@ module HttpRequest
|
|
27
36
|
end
|
28
37
|
end
|
29
38
|
|
39
|
+
def ssl?
|
40
|
+
uri.normalized_scheme == "https"
|
41
|
+
end
|
42
|
+
|
30
43
|
def close
|
31
44
|
http.finish if http
|
32
45
|
end
|
@@ -45,7 +58,7 @@ module HttpRequest
|
|
45
58
|
end
|
46
59
|
|
47
60
|
def server_id
|
48
|
-
uri.
|
61
|
+
uri.normalized_site
|
49
62
|
end
|
50
63
|
|
51
64
|
[:get, :post, :put, :delete, :head].each do |verb|
|
@@ -66,7 +79,7 @@ module HttpRequest
|
|
66
79
|
else
|
67
80
|
# Update the path and query
|
68
81
|
# Note: also normalize the path in case its malformed.. dum dums
|
69
|
-
uri.join! parse_uri(path.gsub('//','/')).request_uri
|
82
|
+
uri.join! parse_uri(path.to_s.gsub('//','/')).request_uri
|
70
83
|
end
|
71
84
|
|
72
85
|
# Open the connection if its closed or hasn't been started..
|
@@ -85,49 +98,28 @@ module HttpRequest
|
|
85
98
|
# Make the HTTP request
|
86
99
|
retries = max_retries
|
87
100
|
begin
|
88
|
-
|
101
|
+
response = http.request(req)
|
89
102
|
rescue EOFError, Errno::EPIPE
|
90
103
|
# Something happened to our connection, lets try this again
|
91
104
|
if retries > 0
|
92
105
|
retries -= 1
|
93
106
|
open
|
94
107
|
retry
|
95
|
-
end
|
108
|
+
end
|
96
109
|
end
|
97
110
|
|
98
111
|
# Let's make sure we close the connection if we don't intend to
|
99
112
|
# have persistent connections..
|
100
113
|
close if !keep_alive
|
101
114
|
|
102
|
-
|
103
|
-
response = Response.new(raw_response, uri)
|
104
|
-
|
105
|
-
# TODO: what about redirects that go back and forth..
|
106
|
-
# we should introduce a max number of auto_redirects..
|
107
|
-
|
108
|
-
# Handle URL Redirection
|
109
|
-
# TODO: better redirection support.. some bugs / cases here..
|
110
|
-
if auto_redirect && raw_response.kind_of?(Net::HTTPRedirection) && !response.header['location'].nil?
|
111
|
-
# binding.pry
|
112
|
-
new_location = response.header['location']
|
113
|
-
if new_location =~ /^http/i
|
114
|
-
close # close existing connection..
|
115
|
-
@uri = parse_uri(new_location)
|
116
|
-
else
|
117
|
-
uri.join! parse_uri(new_location.gsub('//','/')).request_uri
|
118
|
-
end
|
119
|
-
|
120
|
-
request(verb, uri.request_uri, headers, body)
|
121
|
-
else
|
122
|
-
response
|
123
|
-
end
|
115
|
+
response
|
124
116
|
end
|
125
117
|
|
126
118
|
|
127
119
|
private
|
128
120
|
|
129
|
-
def parse_uri(
|
130
|
-
Addressable::URI.parse(Addressable::URI.encode(Addressable::URI.unencode(
|
121
|
+
def parse_uri(url)
|
122
|
+
Addressable::URI.parse(Addressable::URI.encode(Addressable::URI.unencode(url)))
|
131
123
|
end
|
132
124
|
|
133
125
|
end
|
data/lib/http-request/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http-request
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: addressable
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '2.
|
21
|
+
version: '2.3'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '2.
|
29
|
+
version: '2.3'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rake
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,7 +72,6 @@ files:
|
|
72
72
|
- lib/http-request/response.rb
|
73
73
|
- lib/http-request/version.rb
|
74
74
|
- lib/http-request.rb
|
75
|
-
- lib/old.rb
|
76
75
|
homepage: http://nulayer.com
|
77
76
|
licenses: []
|
78
77
|
post_install_message:
|
@@ -87,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
86
|
version: '0'
|
88
87
|
segments:
|
89
88
|
- 0
|
90
|
-
hash:
|
89
|
+
hash: 4098152367455332880
|
91
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
91
|
none: false
|
93
92
|
requirements:
|
data/lib/old.rb
DELETED
@@ -1,198 +0,0 @@
|
|
1
|
-
# +-------------------------------------------------------------------------------+
|
2
|
-
# | HttpRequest |
|
3
|
-
# +-------------------------------------------------------------------------------+
|
4
|
-
# | Author: Nulayer Inc. / www.nulayer.com |
|
5
|
-
# +-------------------------------------------------------------------------------+
|
6
|
-
# | BSD LICENSE |
|
7
|
-
# | Copyright (c) 2007-2011, Nulayer Inc. |
|
8
|
-
# | All rights reserved. |
|
9
|
-
# | |
|
10
|
-
# | Redistribution and use in source and binary forms, with or without |
|
11
|
-
# | modification, are permitted provided that the following conditions are met: |
|
12
|
-
# | * Redistributions of source code must retain the above copyright |
|
13
|
-
# | notice, this list of conditions and the following disclaimer. |
|
14
|
-
# | * Redistributions in binary form must reproduce the above copyright |
|
15
|
-
# | notice, this list of conditions and the following disclaimer in the |
|
16
|
-
# | documentation and/or other materials provided with the distribution. |
|
17
|
-
# | * Neither the name of the <organization> nor the |
|
18
|
-
# | names of its contributors may be used to endorse or promote products |
|
19
|
-
# | derived from this software without specific prior written permission. |
|
20
|
-
# | |
|
21
|
-
# | THIS SOFTWARE IS PROVIDED BY NuLayer Inc. "AS IS" AND ANY |
|
22
|
-
# | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
23
|
-
# | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
24
|
-
# | DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY |
|
25
|
-
# | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
26
|
-
# | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
27
|
-
# | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
28
|
-
# | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
29
|
-
# | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
30
|
-
# | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
31
|
-
# +-------------------------------------------------------------------------------+
|
32
|
-
|
33
|
-
require 'net/http'
|
34
|
-
|
35
|
-
class HttpRequest
|
36
|
-
VERSION = '1.0.5'
|
37
|
-
|
38
|
-
class HTTPError < StandardError; end
|
39
|
-
class UnexpectedError < StandardError; end
|
40
|
-
|
41
|
-
class UnreachableError < HTTPError; end
|
42
|
-
class ConnectionResetError < HTTPError; end
|
43
|
-
class TimeoutError < HTTPError; end
|
44
|
-
class RedirectError < HTTPError; end
|
45
|
-
|
46
|
-
attr_accessor :uri
|
47
|
-
attr_accessor :header
|
48
|
-
attr_accessor :body
|
49
|
-
attr_accessor :status
|
50
|
-
|
51
|
-
class << self
|
52
|
-
attr_accessor :user_agent, :timeout, :max_retries
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
def self.head(url, options={})
|
57
|
-
http = HttpRequest.new(url)
|
58
|
-
http.head(options)
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.get(url, options={})
|
62
|
-
http = HttpRequest.new(url)
|
63
|
-
http.get(options)
|
64
|
-
end
|
65
|
-
|
66
|
-
def initialize(url)
|
67
|
-
self.uri = url
|
68
|
-
end
|
69
|
-
|
70
|
-
def head(options={})
|
71
|
-
request(:head, options)
|
72
|
-
end
|
73
|
-
|
74
|
-
def get(options={})
|
75
|
-
request(:get, options)
|
76
|
-
end
|
77
|
-
|
78
|
-
def request(type, options={})
|
79
|
-
user_agent = options[:user_agent] || HttpRequest.user_agent || 'HttpRequest'
|
80
|
-
timeout = (options[:timeout] || HttpRequest.timeout || 10).to_i
|
81
|
-
redirect_limit = options[:redirect_limit] || 5
|
82
|
-
output_file = options[:output_file]
|
83
|
-
content_max = options[:content_max]
|
84
|
-
max_retries = HttpRequest.max_retries || 3
|
85
|
-
|
86
|
-
# Check if we're in a redirecting loop
|
87
|
-
if redirect_limit == 0
|
88
|
-
raise RedirectError, "HTTP redirect too deep: #{uri.to_s}"
|
89
|
-
end
|
90
|
-
|
91
|
-
# Prepare the request
|
92
|
-
req_path = uri.path
|
93
|
-
req_path += "?#{uri.query}" if !uri.query.nil?
|
94
|
-
|
95
|
-
req_klass = (type == :head ? Net::HTTP::Head : Net::HTTP::Get)
|
96
|
-
req = req_klass.new(req_path, { 'User-Agent' => user_agent })
|
97
|
-
|
98
|
-
# Basic authentication
|
99
|
-
if uri.user && uri.password
|
100
|
-
req.basic_auth uri.user, uri.password
|
101
|
-
end
|
102
|
-
|
103
|
-
# HTTP request block
|
104
|
-
@retries = max_retries
|
105
|
-
begin
|
106
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
107
|
-
|
108
|
-
http.open_timeout = timeout
|
109
|
-
http.read_timeout = timeout
|
110
|
-
|
111
|
-
http.start do |http|
|
112
|
-
http.request(req) do |response|
|
113
|
-
# Save the header
|
114
|
-
self.header = response.header
|
115
|
-
self.body = nil
|
116
|
-
|
117
|
-
# Restrict downloading of content
|
118
|
-
download_content = true
|
119
|
-
if !content_max.nil? && header['content-length'].to_i > content_max
|
120
|
-
download_content = false
|
121
|
-
end
|
122
|
-
|
123
|
-
# Get the body
|
124
|
-
if download_content
|
125
|
-
if output_file
|
126
|
-
open(output_file, "w") do |f|
|
127
|
-
response.read_body { |data| f.write(data) }
|
128
|
-
end
|
129
|
-
else
|
130
|
-
self.body = response.read_body
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Handle URL Redirection
|
135
|
-
if response.kind_of?(Net::HTTPRedirection) && !header['location'].nil?
|
136
|
-
update_uri_location(header['location'])
|
137
|
-
request type, options.merge({ :redirect_limit => redirect_limit-1 })
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
rescue ::Errno::ENETUNREACH
|
142
|
-
raise UnreachableError, "network is unreachable: #{uri.to_s}"
|
143
|
-
rescue ::Errno::ECONNRESET, ::Errno::ECONNREFUSED, ::SocketError
|
144
|
-
raise ConnectionResetError, "connection reset by peer: #{uri.to_s}"
|
145
|
-
rescue ::Timeout::Error
|
146
|
-
raise TimeoutError, "connection timed out: #{uri.to_s}"
|
147
|
-
rescue ::Errno::EINTR => ex
|
148
|
-
# Transmission was interrupted, retry the request just once.
|
149
|
-
# This usually occurs because the process received a signal.
|
150
|
-
@retries -= 1
|
151
|
-
if @retries >= 0
|
152
|
-
retry
|
153
|
-
else
|
154
|
-
raise ex
|
155
|
-
end
|
156
|
-
rescue HTTPError => ex
|
157
|
-
# Catch exceptions that may be raised above -- not sure why we have to do this
|
158
|
-
raise ex
|
159
|
-
rescue => ex
|
160
|
-
raise UnexpectedError, "#{ex.inspect} -- #{uri.to_s}"
|
161
|
-
ensure
|
162
|
-
http.finish rescue nil
|
163
|
-
http = nil
|
164
|
-
end
|
165
|
-
|
166
|
-
return self
|
167
|
-
end
|
168
|
-
|
169
|
-
def header=(header)
|
170
|
-
return if header.nil?
|
171
|
-
@header = header
|
172
|
-
self.status = header.code.to_i
|
173
|
-
end
|
174
|
-
|
175
|
-
def reset!
|
176
|
-
self.header = nil
|
177
|
-
self.body = nil
|
178
|
-
self.status = nil
|
179
|
-
end
|
180
|
-
|
181
|
-
def uri=(url)
|
182
|
-
@uri = URI.parse(url)
|
183
|
-
@uri.path = '/' if @uri.path.empty?
|
184
|
-
reset!
|
185
|
-
end
|
186
|
-
|
187
|
-
def update_uri_location(location)
|
188
|
-
if header['location'].to_s[0].to_s.chr == '/'
|
189
|
-
url = "#{uri.scheme}://#{uri.host}"
|
190
|
-
url += ":#{uri.port}" if uri.port != 80 && uri.port != 443
|
191
|
-
url += location
|
192
|
-
else
|
193
|
-
url = location
|
194
|
-
end
|
195
|
-
|
196
|
-
self.uri = url
|
197
|
-
end
|
198
|
-
end
|