http-request 1.0.5 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{README → README.md} +2 -3
- data/lib/http-request.rb +14 -191
- data/lib/http-request/client.rb +71 -0
- data/lib/http-request/connection.rb +119 -0
- data/lib/http-request/response.rb +16 -0
- data/lib/http-request/version.rb +3 -0
- data/lib/old.rb +198 -0
- metadata +58 -5
- data/VERSION +0 -1
data/{README → README.md}
RENAMED
data/lib/http-request.rb
CHANGED
@@ -1,198 +1,21 @@
|
|
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
1
|
require 'net/http'
|
2
|
+
require 'addressable/uri'
|
34
3
|
|
35
|
-
|
36
|
-
|
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
|
4
|
+
module HttpRequest
|
5
|
+
extend self
|
85
6
|
|
86
|
-
|
87
|
-
|
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
|
7
|
+
def pool(key)
|
8
|
+
Client.pool(key)
|
179
9
|
end
|
180
10
|
|
181
|
-
def
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
11
|
+
def open(url, options={})
|
12
|
+
client = Client.new(options)
|
13
|
+
client.open(url)
|
14
|
+
client
|
197
15
|
end
|
198
16
|
end
|
17
|
+
|
18
|
+
require 'http-request/version'
|
19
|
+
require 'http-request/client'
|
20
|
+
require 'http-request/response'
|
21
|
+
require 'http-request/connection'
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module HttpRequest
|
2
|
+
class Client
|
3
|
+
attr_reader :connections, :options
|
4
|
+
|
5
|
+
# Syntax sugar to manage clients in the current thread
|
6
|
+
def self.pool(key)
|
7
|
+
key = "http-request:#{key}"
|
8
|
+
Thread.current[key] ||= Client.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.delete_from_pool(key)
|
12
|
+
Thread.current[key] = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(options={})
|
16
|
+
@options = options # Connection options
|
17
|
+
@connections = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def open(url)
|
21
|
+
# Let's close any dead connections as we open new ones
|
22
|
+
cleanup_dead_connections!
|
23
|
+
|
24
|
+
# Reuse existing connection or open a new one..
|
25
|
+
conn = connection(url)
|
26
|
+
if conn.nil? || conn.closed?
|
27
|
+
conn = Connection.new(url, options)
|
28
|
+
connections << conn
|
29
|
+
end
|
30
|
+
conn
|
31
|
+
end
|
32
|
+
|
33
|
+
def close(url=nil)
|
34
|
+
if !url.nil?
|
35
|
+
conn = connection(url)
|
36
|
+
conn.close
|
37
|
+
connections.delete(conn)
|
38
|
+
else
|
39
|
+
connections.each {|conn| conn.close }
|
40
|
+
@connections = []
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
[:get, :post, :put, :delete, :head].each do |verb|
|
45
|
+
define_method(verb) do |*args|
|
46
|
+
url, headers, body = args
|
47
|
+
request(verb, url, headers || {}, body)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def request(method, url, headers={}, body=nil)
|
52
|
+
conn = open(url)
|
53
|
+
conn.send method, url, headers, body
|
54
|
+
end
|
55
|
+
|
56
|
+
def connection(url)
|
57
|
+
connections.find {|conn| conn.server_id == server_id(url) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def server_id(url)
|
61
|
+
Addressable::URI.parse(url).origin
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def cleanup_dead_connections!
|
67
|
+
dead_conns = connections.collect {|conn| conn if conn.closed? }.compact
|
68
|
+
dead_conns.each {|conn| @connections.delete(conn) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module HttpRequest
|
2
|
+
class Connection
|
3
|
+
attr_reader :uri, :http
|
4
|
+
attr_accessor :user_agent, :max_retries, :timeout, :auto_redirect, :keep_alive
|
5
|
+
|
6
|
+
def initialize(url, options={})
|
7
|
+
# Defaults
|
8
|
+
@user_agent = options[:user_agent] || "HttpRequest v#{HttpRequest::VERSION}"
|
9
|
+
@max_retries = options[:max_retries] || 3
|
10
|
+
@timeout = options[:timeout] || 30 # 30 second timeout by default
|
11
|
+
@auto_redirect = options[:auto_redirect].nil? ? true : options[:auto_redirect]
|
12
|
+
@keep_alive = options[:keep_alive].nil? ? true : options[:keep_alive]
|
13
|
+
|
14
|
+
# Let's do this
|
15
|
+
@uri = Addressable::URI.parse(url)
|
16
|
+
open
|
17
|
+
end
|
18
|
+
|
19
|
+
def open
|
20
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
21
|
+
http.open_timeout = timeout
|
22
|
+
http.read_timeout = timeout
|
23
|
+
http.start
|
24
|
+
|
25
|
+
if Socket.const_defined?(:TCP_NODELAY)
|
26
|
+
socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def close
|
31
|
+
http.finish if http
|
32
|
+
end
|
33
|
+
|
34
|
+
def opened?
|
35
|
+
!closed?
|
36
|
+
end
|
37
|
+
|
38
|
+
def closed?
|
39
|
+
!(!http.nil? && http.started?)
|
40
|
+
end
|
41
|
+
|
42
|
+
def socket
|
43
|
+
return if http.nil?
|
44
|
+
http.instance_variable_get(:@socket)
|
45
|
+
end
|
46
|
+
|
47
|
+
def server_id
|
48
|
+
uri.origin
|
49
|
+
end
|
50
|
+
|
51
|
+
[:get, :post, :put, :delete, :head].each do |verb|
|
52
|
+
define_method(verb) do |*args|
|
53
|
+
path, headers, body = args
|
54
|
+
request(verb, path, headers || {}, body)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def request(verb, path, headers={}, body=nil)
|
59
|
+
if path =~ /^http/i
|
60
|
+
new_uri = Addressable::URI.parse(path)
|
61
|
+
if new_uri.origin != uri.origin
|
62
|
+
# TODO.. get an error class for this..
|
63
|
+
raise "Ummm... you're trying to talk to another server you duffis.."
|
64
|
+
end
|
65
|
+
uri.path = new_uri.path
|
66
|
+
end
|
67
|
+
|
68
|
+
# Open the connection if its closed or hasn't been started..
|
69
|
+
open if closed?
|
70
|
+
|
71
|
+
# headers['Date'] = Time.now.httpdate # need this..?
|
72
|
+
headers['User-Agent'] ||= user_agent
|
73
|
+
headers['Connection'] ||= 'keep-alive' if keep_alive
|
74
|
+
headers['Content-Length'] ||= body.bytesize.to_s if body
|
75
|
+
|
76
|
+
# TODO.. link traversal..
|
77
|
+
# ..will our host change tho... like the thing we actually connect to?
|
78
|
+
# it could very well... so we should check that out..
|
79
|
+
# maybe we have a "host" or "host_id" variable for this stuff..?
|
80
|
+
|
81
|
+
# Build request
|
82
|
+
req_klass = instance_eval("Net::HTTP::"+verb.to_s.capitalize)
|
83
|
+
req = req_klass.new(uri.to_s, headers)
|
84
|
+
req.body = body if !body.nil? && !body.empty?
|
85
|
+
|
86
|
+
# Make the HTTP request
|
87
|
+
retries = max_retries
|
88
|
+
begin
|
89
|
+
raw_response = http.request(req)
|
90
|
+
rescue EOFError, Errno::EPIPE
|
91
|
+
# Something happened to our connection, lets try this again
|
92
|
+
if retries > 0
|
93
|
+
retries -= 1
|
94
|
+
open
|
95
|
+
retry
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Let's make sure we close the connection if we don't intend to
|
100
|
+
# have persistent connections..
|
101
|
+
close if !keep_alive
|
102
|
+
|
103
|
+
# Build response
|
104
|
+
response = Response.new(raw_response, uri)
|
105
|
+
|
106
|
+
# TODO: what about redirects that go back and forth..
|
107
|
+
# should we introduce a max number of auto_redirects..?
|
108
|
+
|
109
|
+
# Handle URL Redirection
|
110
|
+
if auto_redirect && raw_response.kind_of?(Net::HTTPRedirection) && !response.header['location'].nil?
|
111
|
+
@uri = Addressable::URI.parse(response.header['location'])
|
112
|
+
request(verb, path, headers, body)
|
113
|
+
else
|
114
|
+
response
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HttpRequest
|
2
|
+
class Response
|
3
|
+
attr_reader :status, :header, :body, :raw, :request_uri
|
4
|
+
|
5
|
+
def initialize(net_http_response, request_uri)
|
6
|
+
@status = net_http_response.code.to_i
|
7
|
+
@body = net_http_response.body
|
8
|
+
@raw = net_http_response
|
9
|
+
@request_uri = request_uri
|
10
|
+
|
11
|
+
@header = net_http_response.header.to_hash.tap do |header|
|
12
|
+
header.each {|k,v| header[k] = v.first if v.is_a?(Array) && v.length == 1 }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/old.rb
ADDED
@@ -0,0 +1,198 @@
|
|
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
|
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.0
|
4
|
+
version: 1.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,56 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-07-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: addressable
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.2'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.7.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.7.0
|
14
62
|
description: Simple HTTP client built with Net/HTTP
|
15
63
|
email:
|
16
64
|
- peter.kieltyka@nulayer.com
|
@@ -18,9 +66,13 @@ executables: []
|
|
18
66
|
extensions: []
|
19
67
|
extra_rdoc_files: []
|
20
68
|
files:
|
21
|
-
- README
|
22
|
-
-
|
69
|
+
- README.md
|
70
|
+
- lib/http-request/client.rb
|
71
|
+
- lib/http-request/connection.rb
|
72
|
+
- lib/http-request/response.rb
|
73
|
+
- lib/http-request/version.rb
|
23
74
|
- lib/http-request.rb
|
75
|
+
- lib/old.rb
|
24
76
|
homepage: http://nulayer.com
|
25
77
|
licenses: []
|
26
78
|
post_install_message:
|
@@ -46,3 +98,4 @@ signing_key:
|
|
46
98
|
specification_version: 3
|
47
99
|
summary: Simple HTTP client built with Net/HTTP
|
48
100
|
test_files: []
|
101
|
+
has_rdoc:
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.0.5
|