http-request 1.1.6 → 1.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'
@@ -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 method, url, headers, body
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
- Addressable::URI.parse(url).origin
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
- # 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]
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.origin
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
- raw_response = http.request(req)
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
- # Build response
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(uri)
130
- Addressable::URI.parse(Addressable::URI.encode(Addressable::URI.unencode(uri)))
121
+ def parse_uri(url)
122
+ Addressable::URI.parse(Addressable::URI.encode(Addressable::URI.unencode(url)))
131
123
  end
132
124
 
133
125
  end
@@ -1,3 +1,3 @@
1
1
  module HttpRequest
2
- VERSION = '1.1.6'
2
+ VERSION = '1.1.8'
3
3
  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.1.6
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-08 00:00:00.000000000 Z
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.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.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: -4505057513829640071
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