httpi 2.4.1 → 2.4.2

Sign up to get free protection for your applications and to get access to all the features.
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