httpi 2.4.1 → 2.4.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e6ca1f335780f0839e9e5316c9ecf1a6d3753bea
4
- data.tar.gz: f5e52a1dde48d6c783c2bc66b691df4cb4e1f4cd
3
+ metadata.gz: b00e0eb6313b47ac5eb3be9f3330bac10a9321b7
4
+ data.tar.gz: b49e8aaba759e1384bae07bf825b17f511c809ec
5
5
  SHA512:
6
- metadata.gz: f7e7e5d0cf5c2a2f59ad4b8bf331c4917cfcef503c0e905b6613b08ad03a8a23e20727668b51a3e27762167905b5a3c1a526a9fb987cd1d528dd6429090ee98c
7
- data.tar.gz: ffe4093b24fc493cda6c8b7e266a5abcdc97785759e59024a6f02ef984f15b9c6707e70dfbd76e7f29bd91ed5077af219bd5c593d99bc675f720024e467193c6
6
+ metadata.gz: b3a31c1b2ac5ebbb82336104e64130f3f214462838175c7d966677c5e1cb03b77273d80b0282e9a1a032ea38b963215974a29777223c0da287d7755418b1b05a
7
+ data.tar.gz: 1c9baad44d8900f1bad06e9c991079300541476dded64e9bd44468a77552fc8d1143c6abe1d6e1ef42c11adf6427ed1d4241f02f9538fccb820e83b985bafc1a
@@ -1,8 +1,9 @@
1
1
  language: "ruby"
2
2
  script: "bundle exec rake ci"
3
+ sudo: false
3
4
  rvm:
4
- - 1.9.3
5
- - 2.0
6
- - 2.1
7
- - 2.2
8
- - jruby-19mode
5
+ - 2.0.0
6
+ - 2.1.8
7
+ - 2.2.4
8
+ - 2.3.0
9
+ - jruby
@@ -1,3 +1,17 @@
1
+ ### 2.4.2
2
+
3
+ * Feature: [#165](https://github.com/savonrb/httpi/pull/165) Extended net_http adapter ssl options with cert_store and ca_path
4
+ * Improvement: [#166](https://github.com/savonrb/httpi/pull/166) fix some warnings
5
+ * Feature: [#167](https://github.com/savonrb/httpi/pull/167) adds missing support for cert password
6
+ * Feature: [#163](https://github.com/savonrb/httpi/pull/163) Support HTTP Basic auth in Rack adapter
7
+ * Fix: [#162](https://github.com/savonrb/httpi/pull/162) fixing excon adapter to pass client_cert and key as strings instead of objects
8
+ * Fix: [#160](https://github.com/savonrb/httpi/pull/160) httpclient adapter now accepts NTLM auth
9
+ * Improvement: [#158](https://github.com/savonrb/httpi/pull/158) Limit the maximum of redirects to prevent infinite redirects
10
+ * Improvement: [#153](https://github.com/savonrb/httpi/pull/153) Dynamically determine methods Net::HTTP supports
11
+ * Improvement: [#155](https://github.com/savonrb/httpi/pull/155) Enable socks proxy server specification
12
+ * Improvement: [#151](https://github.com/savonrb/httpi/pull/151) Fix excon verify mode
13
+ * Fix: [#149](https://github.com/savonrb/httpi/pull/149) Ensure that header exists before accessing it
14
+
1
15
  ### 2.4.1
2
16
 
3
17
  * Fix: [#147](https://github.com/savonrb/httpi/pull/147) Fix call Curb client "SSL peer certificate or SSH remote key was not OK" bug
data/Gemfile CHANGED
@@ -8,7 +8,7 @@ gem 'httpclient', '~> 2.3', :require => false
8
8
  gem 'curb', '~> 0.8', :require => false, :platforms => :ruby
9
9
  gem 'em-http-request', :require => false, :platforms => [:ruby, :jruby]
10
10
  gem 'em-synchrony', :require => false, :platforms => [:ruby, :jruby]
11
- gem 'excon', '~> 0.21.0', :require => false, :platforms => [:ruby, :jruby]
11
+ gem 'excon', '~> 0.21', :require => false, :platforms => [:ruby, :jruby]
12
12
  gem 'net-http-persistent', '~> 2.8', :require => false
13
13
  gem 'http', :require => false
14
14
 
@@ -17,12 +17,14 @@ Gem::Specification.new do |s|
17
17
  s.license = 'MIT'
18
18
 
19
19
  s.add_dependency 'rack'
20
+ s.add_dependency 'socksify'
20
21
 
21
22
  s.add_development_dependency 'rubyntlm', '~> 0.3.2'
22
23
  s.add_development_dependency 'rake', '~> 10.0'
23
24
  s.add_development_dependency 'rspec', '~> 2.14'
24
25
  s.add_development_dependency 'mocha', '~> 0.13'
25
26
  s.add_development_dependency 'puma', '~> 2.3.2'
27
+ s.add_development_dependency 'webmock'
26
28
 
27
29
  s.files = `git ls-files`.split("\n")
28
30
  s.require_path = 'lib'
@@ -103,7 +103,7 @@ module HTTPI
103
103
  class << self
104
104
 
105
105
  def query_builder
106
- @query_builder || HTTPI::QueryBuilder::Flat
106
+ @query_builder ||= HTTPI::QueryBuilder::Flat
107
107
  end
108
108
 
109
109
  def query_builder=(builder)
@@ -152,7 +152,7 @@ module HTTPI
152
152
  end
153
153
 
154
154
  # Executes an HTTP request for the given +method+.
155
- def request(method, request, adapter = nil)
155
+ def request(method, request, adapter = nil, redirects = 0)
156
156
  adapter_class = load_adapter(adapter, request)
157
157
 
158
158
  yield adapter_class.client if block_given?
@@ -160,10 +160,10 @@ module HTTPI
160
160
 
161
161
  response = adapter_class.request(method)
162
162
 
163
- if response && HTTPI::Response::RedirectResponseCodes.member?(response.code) && request.follow_redirect?
163
+ if response && HTTPI::Response::RedirectResponseCodes.member?(response.code) && request.follow_redirect? && redirects < request.redirect_limit
164
164
  request.url = URI.join(request.url, response.headers['location'])
165
165
  log("Following redirect: '#{request.url}'.")
166
- return request(method, request, adapter)
166
+ return request(method, request, adapter, redirects + 1)
167
167
  end
168
168
 
169
169
  response
@@ -114,6 +114,7 @@ module HTTPI
114
114
  # Send client-side certificate regardless of state of SSL verify mode
115
115
  @client.cert_key = ssl.cert_key_file
116
116
  @client.cert = ssl.cert_file
117
+ @client.certpassword = ssl.cert_key_password
117
118
 
118
119
  @client.ssl_verify_peer = ssl.verify_mode == :peer
119
120
  end
@@ -61,11 +61,14 @@ module HTTPI
61
61
  opts[:response_block] = @request.on_body if @request.on_body
62
62
  opts[:proxy] = @request.proxy if @request.proxy
63
63
 
64
- if ssl.verify_mode == :peer
64
+ case ssl.verify_mode
65
+ when :peer
65
66
  opts[:ssl_verify_peer] = true
66
67
  opts[:ssl_ca_file] = ssl.ca_cert_file if ssl.ca_cert_file
67
- opts[:client_cert] = ssl.cert if ssl.cert
68
- opts[:client_key] = ssl.cert_key if ssl.cert_key
68
+ opts[:certificate] = ssl.cert.to_pem if ssl.cert
69
+ opts[:private_key] = ssl.cert_key.to_pem if ssl.cert_key
70
+ when :none
71
+ opts[:ssl_verify_peer] = false
69
72
  end
70
73
 
71
74
  opts[:ssl_version] = ssl.ssl_version if ssl.ssl_version
@@ -36,11 +36,8 @@ module HTTPI
36
36
  def setup_client
37
37
  basic_setup
38
38
 
39
- if @request.auth.ntlm?
40
- raise NotSupportedError, "HTTPClient adapter does not support NTLM authentication"
41
- end
42
-
43
39
  setup_auth if @request.auth.http?
40
+ setup_ntlm_auth if @request.auth.ntlm?
44
41
  setup_ssl_auth if @request.auth.ssl? || @request.ssl?
45
42
  end
46
43
 
@@ -54,6 +51,16 @@ module HTTPI
54
51
  @client.set_auth @request.url, *@request.auth.credentials
55
52
  end
56
53
 
54
+ def setup_ntlm_auth
55
+ username, password, domain = @request.auth.credentials
56
+
57
+ unless domain.nil?
58
+ username = "#{domain.upcase}\\#{username}"
59
+ end
60
+
61
+ @client.set_auth @request.url, username, password
62
+ end
63
+
57
64
  def setup_ssl_auth
58
65
  ssl = @request.auth.ssl
59
66
 
@@ -4,6 +4,8 @@ require "httpi/adapter/base"
4
4
  require "httpi/response"
5
5
  require 'kconv'
6
6
  require 'socket'
7
+ require "socksify"
8
+ require 'socksify/http'
7
9
 
8
10
  module HTTPI
9
11
  module Adapter
@@ -17,7 +19,6 @@ module HTTPI
17
19
  register :net_http, :deps => %w(net/https)
18
20
  def initialize(request)
19
21
  check_net_ntlm_version!
20
-
21
22
  @request = request
22
23
  @client = create_client
23
24
  end
@@ -27,8 +28,12 @@ module HTTPI
27
28
  # Executes arbitrary HTTP requests.
28
29
  # @see HTTPI.request
29
30
  def request(method)
30
- unless REQUEST_METHODS.include? method
31
- raise NotSupportedError, "Net::HTTP does not support custom HTTP methods"
31
+ # Determine if Net::HTTP supports the method using reflection
32
+ unless Net::HTTP.const_defined?(:"#{method.to_s.capitalize}") &&
33
+ Net::HTTP.const_get(:"#{method.to_s.capitalize}").class == Class
34
+
35
+ raise NotSupportedError, "Net::HTTP does not support "\
36
+ "#{method.to_s.upcase}"
32
37
  end
33
38
  do_request(method) do |http, http_request|
34
39
  http_request.body = @request.body
@@ -67,7 +72,11 @@ module HTTPI
67
72
 
68
73
  def create_client
69
74
  proxy_url = @request.proxy || URI("")
70
- proxy = Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
75
+ if URI(proxy_url).scheme == 'socks'
76
+ proxy =Net::HTTP.SOCKSProxy(proxy_url.host, proxy_url.port)
77
+ else
78
+ proxy = Net::HTTP::Proxy(proxy_url.host, proxy_url.port, proxy_url.user, proxy_url.password)
79
+ end
71
80
  proxy.new(@request.url.host, @request.url.port)
72
81
  end
73
82
 
@@ -92,9 +101,9 @@ module HTTPI
92
101
 
93
102
  # first figure out if we should use NTLM or Negotiate
94
103
  nego_auth_response = respond_with(requester.call(http, request_client(:head)))
95
- if nego_auth_response.headers['www-authenticate'].include? 'Negotiate'
104
+ if nego_auth_response.headers['www-authenticate'] && nego_auth_response.headers['www-authenticate'].include?('Negotiate')
96
105
  auth_method = 'Negotiate'
97
- elsif nego_auth_response.headers['www-authenticate'].include? 'NTLM'
106
+ elsif nego_auth_response.headers['www-authenticate'] && nego_auth_response.headers['www-authenticate'].include?('NTLM')
98
107
  auth_method = 'NTLM'
99
108
  else
100
109
  auth_method = 'NTLM'
@@ -150,6 +159,8 @@ module HTTPI
150
159
  if @request.auth.ssl?
151
160
  unless ssl.verify_mode == :none
152
161
  @client.ca_file = ssl.ca_cert_file if ssl.ca_cert_file
162
+ @client.ca_path = ssl.ca_cert_path if ssl.ca_cert_path
163
+ @client.cert_store = ssl_cert_store(ssl)
153
164
  end
154
165
 
155
166
  # Send client-side certificate regardless of state of SSL verify mode
@@ -162,14 +173,17 @@ module HTTPI
162
173
  @client.ssl_version = ssl.ssl_version if ssl.ssl_version
163
174
  end
164
175
 
176
+ def ssl_cert_store(ssl)
177
+ return ssl.cert_store if ssl.cert_store
178
+
179
+ # Use the default cert store by default, i.e. system ca certs
180
+ cert_store = OpenSSL::X509::Store.new
181
+ cert_store.set_default_paths
182
+ cert_store
183
+ end
184
+
165
185
  def request_client(type)
166
- request_class = case type
167
- when :get then Net::HTTP::Get
168
- when :post then Net::HTTP::Post
169
- when :head then Net::HTTP::Head
170
- when :put then Net::HTTP::Put
171
- when :delete then Net::HTTP::Delete
172
- end
186
+ request_class = Net::HTTP.const_get(:"#{type.to_s.capitalize}")
173
187
 
174
188
  request_client = request_class.new @request.url.request_uri, @request.headers
175
189
  request_client.basic_auth(*@request.auth.credentials) if @request.auth.basic?
@@ -1,3 +1,4 @@
1
+ require 'base64'
1
2
  require 'httpi/adapter/base'
2
3
  require 'httpi/response'
3
4
 
@@ -61,17 +62,18 @@ module HTTPI
61
62
  raise NotSupportedError, "Rack adapter does not support custom HTTP methods"
62
63
  end
63
64
 
64
- env = {}
65
- @request.headers.each do |header, value|
66
- env["HTTP_#{header.gsub('-', '_').upcase}"] = value
67
- end
68
-
69
65
  if @request.proxy
70
66
  raise NotSupportedError, "Rack adapter does not support proxying"
71
67
  end
72
68
 
73
69
  if @request.auth.http?
74
- raise NotSupportedError, "Rack adapter does not support HTTP auth"
70
+ if @request.auth.basic?
71
+ basic_auth = @request.auth.basic.join(':')
72
+ encoded = Base64.encode64(basic_auth).gsub('\n', '')
73
+ @request.headers['Authorization'] = "Basic #{encoded}"
74
+ else
75
+ raise NotSupportedError, "Rack adapter does not support HTTP #{@request.auth.type} auth"
76
+ end
75
77
  end
76
78
 
77
79
  if @request.auth.ssl?
@@ -82,6 +84,11 @@ module HTTPI
82
84
  raise NotSupportedError, "Rack adapter does not support response streaming"
83
85
  end
84
86
 
87
+ env = {}
88
+ @request.headers.each do |header, value|
89
+ env["HTTP_#{header.gsub('-', '_').upcase}"] = value
90
+ end
91
+
85
92
  response = @client.request(method.to_s.upcase, @request.url.to_s,
86
93
  { :fatal => true, :input => @request.body.to_s }.merge(env))
87
94
 
@@ -10,7 +10,7 @@ module HTTPI
10
10
 
11
11
  VERIFY_MODES = [:none, :peer, :fail_if_no_peer_cert, :client_once]
12
12
  CERT_TYPES = [:pem, :der]
13
- SSL_VERSIONS = OpenSSL::SSL::SSLContext::METHODS.reject { |method| method.match /server|client/ }.sort.reverse
13
+ SSL_VERSIONS = OpenSSL::SSL::SSLContext::METHODS.reject { |method| method.match(/server|client/) }.sort.reverse
14
14
 
15
15
  # Returns whether SSL configuration is present.
16
16
  def present?
@@ -31,6 +31,12 @@ module HTTPI
31
31
  # Accessor for the cacert file to validate SSL certificates.
32
32
  attr_accessor :ca_cert_file
33
33
 
34
+ # Accessor for the ca_path to validate SSL certificates.
35
+ attr_accessor :ca_cert_path
36
+
37
+ # ertificate store holds trusted CA certificates used to verify peer certificates.
38
+ attr_accessor :cert_store
39
+
34
40
  # Returns the cert type to validate SSL certificates PEM|DER.
35
41
  def cert_type
36
42
  @cert_type ||= :pem
@@ -63,7 +69,7 @@ module HTTPI
63
69
 
64
70
  # Returns the SSL version number. Defaults to <tt>nil</tt> (auto-negotiate).
65
71
  def ssl_version
66
- @ssl_version
72
+ @ssl_version ||= nil
67
73
  end
68
74
 
69
75
  # Sets the SSL version number. Expects one of <tt>HTTPI::Auth::SSL::SSL_VERSIONS</tt>.
@@ -11,7 +11,7 @@ module HTTPI
11
11
  class Request
12
12
 
13
13
  # Available attribute writers.
14
- ATTRIBUTES = [:url, :proxy, :headers, :body, :open_timeout, :read_timeout, :follow_redirect, :query]
14
+ ATTRIBUTES = [:url, :proxy, :headers, :body, :open_timeout, :read_timeout, :follow_redirect, :redirect_limit, :query]
15
15
 
16
16
  # Accepts a Hash of +args+ to mass assign attributes and authentication credentials.
17
17
  def initialize(args = {})
@@ -56,8 +56,7 @@ module HTTPI
56
56
 
57
57
  # Returns whether to use SSL.
58
58
  def ssl?
59
- return @ssl unless @ssl.nil?
60
- !!(url.to_s =~ /^https/)
59
+ @ssl ||= !!(url.to_s =~ /^https/)
61
60
  end
62
61
 
63
62
  # Sets whether to use SSL.
@@ -102,6 +101,7 @@ module HTTPI
102
101
  # Sets the block to be called while processing the response. The block
103
102
  # accepts a single parameter - the chunked response body.
104
103
  def on_body(&block)
104
+ @on_body ||= nil
105
105
  if block_given? then
106
106
  @on_body = block
107
107
  end
@@ -130,6 +130,13 @@ module HTTPI
130
130
  @follow_redirect ||= false
131
131
  end
132
132
 
133
+ attr_writer :redirect_limit
134
+
135
+ # Returns how many redirects should be followed - defaults to 3 if not set.
136
+ def redirect_limit
137
+ @redirect_limit ||= 3
138
+ end
139
+
133
140
  private
134
141
 
135
142
  # Stores the cookies from past requests.
@@ -139,7 +146,7 @@ module HTTPI
139
146
 
140
147
  # Expects a +url+, validates its validity and returns a +URI+ object.
141
148
  def normalize_url!(url)
142
- raise ArgumentError, "Invalid URL: #{url}" unless url.to_s =~ /^http/
149
+ raise ArgumentError, "Invalid URL: #{url}" unless url.to_s =~ /^http|socks/
143
150
  url.kind_of?(URI) ? url : URI(url)
144
151
  end
145
152
 
@@ -44,12 +44,14 @@ module HTTPI
44
44
 
45
45
  # Returns any DIME attachments.
46
46
  def attachments
47
+ @body ||= nil
47
48
  decode_body unless @body
48
49
  @attachments ||= []
49
50
  end
50
51
 
51
52
  # Returns the HTTP response body.
52
53
  def body
54
+ @body ||= nil
53
55
  decode_body unless @body
54
56
  @body
55
57
  end
@@ -1,3 +1,3 @@
1
1
  module HTTPI
2
- VERSION = '2.4.1'
2
+ VERSION = '2.4.2'
3
3
  end
@@ -20,7 +20,7 @@ unless RUBY_PLATFORM =~ /java/
20
20
  it "supports ntlm authentication" do
21
21
  request = HTTPI::Request.new(@server.url + "ntlm-auth")
22
22
  adapter = HTTPI::Adapter::Curb.new(request)
23
-
23
+
24
24
  request.auth.ntlm("tester", "vReqSoafRe5O")
25
25
  expect(adapter.request(:get).body).to eq("ntlm-auth")
26
26
  end
@@ -257,8 +257,8 @@ unless RUBY_PLATFORM =~ /java/
257
257
  end
258
258
 
259
259
  it 'to 2 when ssl_version is specified as SSLv2/SSLv23' do
260
- version = OpenSSL::SSL::SSLContext::METHODS.reject { |method| method.match /server|client/ }
261
- version = version.select { |method| method.to_s.match /SSLv2|SSLv23/ }.first
260
+ version = OpenSSL::SSL::SSLContext::METHODS.reject { |method| method.match(/server|client/) }
261
+ version = version.select { |method| method.to_s.match(/SSLv2|SSLv23/) }.first
262
262
  request.auth.ssl.ssl_version = version
263
263
  curb.expects(:ssl_version=).with(2)
264
264
 
@@ -279,6 +279,7 @@ unless RUBY_PLATFORM =~ /java/
279
279
  request = HTTPI::Request.new("http://example.com")
280
280
  request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
281
281
  request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
282
+ request.auth.ssl.cert_key_password = 'example'
282
283
  request
283
284
  end
284
285
 
@@ -287,6 +288,7 @@ unless RUBY_PLATFORM =~ /java/
287
288
  curb.expects(:ssl_verify_host=).with(0) # avoid "SSL peer certificate" error
288
289
  curb.expects(:cert_key=).with(request.auth.ssl.cert_key_file)
289
290
  curb.expects(:cert=).with(request.auth.ssl.cert_file)
291
+ curb.expects(:certpassword=).with(request.auth.ssl.cert_key_password)
290
292
 
291
293
  adapter.request(:get)
292
294
  end
@@ -294,6 +296,7 @@ unless RUBY_PLATFORM =~ /java/
294
296
  it "cert_key, cert and ssl_verify_peer should be set" do
295
297
  curb.expects(:cert_key=).with(request.auth.ssl.cert_key_file)
296
298
  curb.expects(:cert=).with(request.auth.ssl.cert_file)
299
+ curb.expects(:certpassword=).with(request.auth.ssl.cert_key_password)
297
300
  curb.expects(:ssl_verify_peer=).with(true)
298
301
  curb.expects(:certtype=).with(request.auth.ssl.cert_type.to_s.upcase)
299
302
 
@@ -67,6 +67,13 @@ describe HTTPI::Adapter::Excon do
67
67
  expect { HTTPI.get(request, adapter) }.
68
68
  to raise_error(HTTPI::NotSupportedError, /does not support NTLM authentication/)
69
69
  end
70
+
71
+ it "supports disabling verify mode" do
72
+ request = HTTPI::Request.new(@server.url)
73
+ request.auth.ssl.verify_mode = :none
74
+ adapter_class = HTTPI::Adapter.load(adapter).new(request)
75
+ expect(adapter_class.client.data[:ssl_verify_peer]).to eq(false)
76
+ end
70
77
  end
71
78
 
72
79
  # it does not support digest auth
@@ -83,9 +90,30 @@ describe HTTPI::Adapter::Excon do
83
90
  end
84
91
 
85
92
  # it does not raise when no certificate was set up
86
- it "works when set up properly" do
93
+ it "works when no client cert is specified" do
94
+ request = HTTPI::Request.new(@server.url)
95
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
96
+
97
+ response = HTTPI.get(request, adapter)
98
+ expect(response.body).to eq("get")
99
+ end
100
+
101
+ it "works with client cert and key provided as file path" do
102
+ request = HTTPI::Request.new(@server.url)
103
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
104
+ request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
105
+ request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
106
+
107
+ response = HTTPI.get(request, adapter)
108
+ expect(response.body).to eq("get")
109
+ end
110
+
111
+ it "works with client cert and key set directly" do
87
112
  request = HTTPI::Request.new(@server.url)
113
+
88
114
  request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
115
+ request.auth.ssl.cert = OpenSSL::X509::Certificate.new File.open("spec/fixtures/client_cert.pem").read
116
+ request.auth.ssl.cert_key = OpenSSL::PKey.read File.open("spec/fixtures/client_key.pem").read
89
117
 
90
118
  response = HTTPI.get(request, adapter)
91
119
  expect(response.body).to eq("get")
@@ -1,6 +1,7 @@
1
1
  require "spec_helper"
2
2
  require "httpi/adapter/httpclient"
3
3
  require "httpi/request"
4
+ require "integration/support/server"
4
5
 
5
6
  HTTPI::Adapter.load_adapter(:httpclient)
6
7
 
@@ -10,6 +11,14 @@ describe HTTPI::Adapter::HTTPClient do
10
11
  let(:ssl_config) { HTTPClient::SSLConfig.any_instance }
11
12
  let(:request) { HTTPI::Request.new("http://example.com") }
12
13
 
14
+ before :all do
15
+ @server = IntegrationServer.run
16
+ end
17
+
18
+ after :all do
19
+ @server.stop
20
+ end
21
+
13
22
  describe "#request(:get)" do
14
23
  it "returns a valid HTTPI::Response" do
15
24
  httpclient_expects(:get)
@@ -174,11 +183,12 @@ describe HTTPI::Adapter::HTTPClient do
174
183
  end
175
184
  end
176
185
 
177
- it "does not support NTLM authentication" do
178
- request.auth.ntlm("tester", "vReqSoafRe5O")
186
+ it "supports NTLM authentication" do
187
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
179
188
 
180
- expect { adapter.request(:get) }.
181
- to raise_error(HTTPI::NotSupportedError, /adapter does not support NTLM authentication/)
189
+ request.auth.ntlm("tester", "vReqSoafRe5O")
190
+ response = HTTPI.get(request, :httpclient)
191
+ expect(response.body).to eq("ntlm-auth")
182
192
  end
183
193
 
184
194
  def httpclient_expects(method)
@@ -14,6 +14,22 @@ describe HTTPI::Adapter::NetHTTP do
14
14
  @server.stop
15
15
  end
16
16
 
17
+ context 'when socks is specified' do
18
+
19
+ let(:socks_client) { mock('socks_client') }
20
+ let(:request){HTTPI::Request.new(@server.url)}
21
+
22
+ it 'uses Net::HTTP.SOCKSProxy as client' do
23
+ socks_client.expects(:new).with(URI(@server.url).host, URI(@server.url).port).returns(:socks_client_instance)
24
+ Net::HTTP.expects(:SOCKSProxy).with('localhost', 8080).returns socks_client
25
+
26
+ request.proxy = 'socks://localhost:8080'
27
+ adapter = HTTPI::Adapter::NetHTTP.new(request)
28
+
29
+ expect(adapter.client).to eq(:socks_client_instance)
30
+ end
31
+ end
32
+
17
33
  it "sends and receives HTTP headers" do
18
34
  request = HTTPI::Request.new(@server.url + "x-header")
19
35
  request.headers["X-Header"] = "HTTPI"
@@ -52,6 +68,36 @@ describe HTTPI::Adapter::NetHTTP do
52
68
  expect(response.headers["Content-Type"]).to eq("text/plain")
53
69
  end
54
70
 
71
+ context "supports custom methods supported by Net::HTTP" do
72
+ let(:request) do
73
+ HTTPI::Request.new(@server.url).tap do|r|
74
+ r.body = request_body if request_body
75
+ end
76
+ end
77
+
78
+ let(:request_body) { nil }
79
+
80
+ let(:response) { HTTPI.request(http_method, request, adapter) }
81
+
82
+ shared_examples_for 'any supported custom method' do
83
+ specify { response.body.should eq http_method.to_s }
84
+ specify { response.headers["Content-Type"].should eq('text/plain') }
85
+ end
86
+
87
+ context 'PATCH' do
88
+ let(:http_method) { :patch }
89
+ let(:request_body) { "<some>xml</some>" }
90
+
91
+ it_behaves_like 'any supported custom method'
92
+ end
93
+
94
+ context 'UNSUPPORTED method' do
95
+ let(:http_method) { :unsupported }
96
+
97
+ specify { expect { response }.to raise_error HTTPI::NotSupportedError }
98
+ end
99
+ end
100
+
55
101
  it "supports basic authentication" do
56
102
  request = HTTPI::Request.new(@server.url + "basic-auth")
57
103
  request.auth.basic("admin", "secret")
@@ -85,6 +131,17 @@ describe HTTPI::Adapter::NetHTTP do
85
131
  expect { HTTPI.get(request, adapter) }.
86
132
  to raise_error(HTTPI::NotSupportedError, /Net::NTLM is not available/)
87
133
  end
134
+
135
+ it "does not crash when authenticate header is missing (on second request)" do
136
+ request = HTTPI::Request.new(@server.url + 'ntlm-auth')
137
+ request.auth.ntlm("tester", "vReqSoafRe5O")
138
+
139
+ expect { HTTPI.get(request, adapter) }.
140
+ to_not raise_error
141
+
142
+ expect { HTTPI.get(request, adapter) }.
143
+ to_not raise_error
144
+ end
88
145
  end
89
146
 
90
147
  # it does not support digest auth
@@ -82,14 +82,12 @@ describe HTTPI::Adapter::NetHTTP do
82
82
  end
83
83
  end
84
84
 
85
- describe "set_auth" do
86
- before do
87
- request.auth.basic "username", "password"
88
- end
89
-
90
- it "is not supported" do
91
- expect { client.request(:get) }.
92
- to raise_error(HTTPI::NotSupportedError, "Rack adapter does not support HTTP auth")
85
+ describe "basic auth" do
86
+ it "is supported" do
87
+ request = HTTPI::Request.new(@url + "basic-auth")
88
+ request.auth.basic("admin", "secret")
89
+ response = HTTPI.get(request, adapter)
90
+ expect(response.body).to eq("basic-auth")
93
91
  end
94
92
  end
95
93
 
@@ -3,7 +3,7 @@ require "httpi/auth/ssl"
3
3
 
4
4
  describe HTTPI::Auth::SSL do
5
5
  before(:all) do
6
- @ssl_versions = OpenSSL::SSL::SSLContext::METHODS.reject { |method| method.match /server|client/ }.sort.reverse
6
+ @ssl_versions = OpenSSL::SSL::SSLContext::METHODS.reject { |method| method.match(/server|client/) }.sort.reverse
7
7
  end
8
8
 
9
9
  describe "VERIFY_MODES" do
@@ -269,6 +269,20 @@ describe HTTPI do
269
269
 
270
270
  client.request(:custom, request, :httpclient)
271
271
  end
272
+
273
+ it 'follows redirects at maximum of the redirect limit' do
274
+ request.follow_redirect = true
275
+ request.redirect_limit = 2
276
+ redirect_location = 'http://foo.bar'
277
+
278
+ redirect = HTTPI::Response.new(302, {'location' => redirect_location}, 'Moved')
279
+ response = HTTPI::Response.new(200, {}, 'success')
280
+
281
+ httpclient.any_instance.expects(:request).times(2).with(:custom).returns(redirect, response)
282
+ request.expects(:url=).with(URI.parse(redirect_location))
283
+
284
+ client.request(:custom, request, :httpclient)
285
+ end
272
286
  end
273
287
 
274
288
  HTTPI::REQUEST_METHODS.each do |method|
@@ -108,6 +108,11 @@ describe HTTPI::Request do
108
108
  expect(request.proxy).to eq(URI("http://proxy.example.com"))
109
109
  end
110
110
 
111
+ it 'also accepts the socks URL to use as a String' do
112
+ request.proxy ="socks://socks.example.com"
113
+ expect(request.proxy).to eq(URI("socks://socks.example.com"))
114
+ end
115
+
111
116
  it "also accepts a URI object" do
112
117
  request.proxy = URI("http://proxy.example.com")
113
118
  expect(request.proxy).to eq(URI("http://proxy.example.com"))
@@ -1,3 +1,4 @@
1
+ require 'rack'
1
2
  require "rack/builder"
2
3
 
3
4
  class IntegrationServer
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpi
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-06-18 00:00:00.000000000 Z
12
+ date: 2016-04-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: socksify
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: rubyntlm
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -95,6 +109,20 @@ dependencies:
95
109
  - - "~>"
96
110
  - !ruby/object:Gem::Version
97
111
  version: 2.3.2
112
+ - !ruby/object:Gem::Dependency
113
+ name: webmock
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
98
126
  description: Common interface for Ruby's HTTP libraries
99
127
  email: me@rubiii.com
100
128
  executables: []
@@ -190,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
218
  version: '0'
191
219
  requirements: []
192
220
  rubyforge_project: httpi
193
- rubygems_version: 2.4.8
221
+ rubygems_version: 2.5.1
194
222
  signing_key:
195
223
  specification_version: 4
196
224
  summary: Common interface for Ruby's HTTP libraries