httpi 2.4.0 → 2.4.5

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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +3 -5
  3. data/CHANGELOG.md +42 -0
  4. data/Gemfile +5 -1
  5. data/README.md +7 -7
  6. data/Rakefile +5 -3
  7. data/httpi.gemspec +2 -1
  8. data/lib/httpi.rb +5 -4
  9. data/lib/httpi/adapter.rb +1 -1
  10. data/lib/httpi/adapter/curb.rb +18 -5
  11. data/lib/httpi/adapter/em_http.rb +5 -4
  12. data/lib/httpi/adapter/excon.rb +16 -5
  13. data/lib/httpi/adapter/http.rb +88 -0
  14. data/lib/httpi/adapter/httpclient.rb +15 -4
  15. data/lib/httpi/adapter/net_http.rb +43 -16
  16. data/lib/httpi/adapter/net_http_persistent.rb +10 -1
  17. data/lib/httpi/adapter/rack.rb +13 -6
  18. data/lib/httpi/auth/ssl.rb +49 -2
  19. data/lib/httpi/logger.rb +6 -1
  20. data/lib/httpi/request.rb +12 -5
  21. data/lib/httpi/response.rb +2 -0
  22. data/lib/httpi/version.rb +1 -1
  23. data/spec/httpi/adapter/curb_spec.rb +56 -18
  24. data/spec/httpi/adapter/em_http_spec.rb +21 -13
  25. data/spec/httpi/adapter/excon_spec.rb +28 -90
  26. data/spec/httpi/adapter/http_spec.rb +28 -0
  27. data/spec/httpi/adapter/httpclient_spec.rb +39 -4
  28. data/spec/httpi/adapter/net_http_persistent_spec.rb +31 -81
  29. data/spec/httpi/adapter/net_http_spec.rb +39 -99
  30. data/spec/httpi/adapter/rack_spec.rb +6 -8
  31. data/spec/httpi/auth/ssl_spec.rb +32 -1
  32. data/spec/httpi/httpi_spec.rb +17 -1
  33. data/spec/httpi/request_spec.rb +5 -0
  34. data/spec/integration/curb_spec.rb +11 -0
  35. data/spec/integration/em_http_spec.rb +17 -0
  36. data/spec/integration/excon_spec.rb +165 -0
  37. data/spec/integration/http_spec.rb +147 -0
  38. data/spec/integration/httpclient_spec.rb +11 -0
  39. data/spec/integration/net_http_persistent_spec.rb +22 -1
  40. data/spec/integration/net_http_spec.rb +127 -1
  41. data/spec/integration/support/application.rb +4 -2
  42. metadata +35 -4
@@ -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,8 @@ 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 = HTTPI::Auth::SSL::SSL_VERSIONS
7
+ @min_max_versions = HTTPI::Auth::SSL::MIN_MAX_VERSIONS
7
8
  end
8
9
 
9
10
  describe "VERIFY_MODES" do
@@ -158,6 +159,36 @@ describe HTTPI::Auth::SSL do
158
159
  end
159
160
  end
160
161
 
162
+ describe "#min_version" do
163
+ subject { HTTPI::Auth::SSL.new }
164
+
165
+ it "returns the min_version" do
166
+ subject.min_version = @min_max_versions.first
167
+ expect(subject.min_version).to eq(@min_max_versions.first)
168
+ end
169
+
170
+ it 'raises ArgumentError if the version is unsupported' do
171
+ expect { ssl.min_version = :ssl_fail }.
172
+ to raise_error(ArgumentError, "Invalid SSL min_version :ssl_fail\n" +
173
+ "Please specify one of #{@min_max_versions}")
174
+ end
175
+ end
176
+
177
+ describe "#max_version" do
178
+ subject { HTTPI::Auth::SSL.new }
179
+
180
+ it "returns the SSL version" do
181
+ subject.max_version = @min_max_versions.first
182
+ expect(subject.max_version).to eq(@min_max_versions.first)
183
+ end
184
+
185
+ it 'raises ArgumentError if the version is unsupported' do
186
+ expect { ssl.max_version = :ssl_fail }.
187
+ to raise_error(ArgumentError, "Invalid SSL max_version :ssl_fail\n" +
188
+ "Please specify one of #{@min_max_versions}")
189
+ end
190
+ end
191
+
161
192
  def ssl
162
193
  ssl = HTTPI::Auth::SSL.new
163
194
  ssl.cert_key_file = "spec/fixtures/client_key.pem"
@@ -4,6 +4,7 @@ require "integration/support/server"
4
4
  # find out why httpi doesn't load these automatically. [dh, 2012-12-15]
5
5
  require "excon"
6
6
  require "net/http/persistent"
7
+ require "http"
7
8
 
8
9
  unless RUBY_VERSION < "1.9"
9
10
  require "em-synchrony"
@@ -268,6 +269,20 @@ describe HTTPI do
268
269
 
269
270
  client.request(:custom, request, :httpclient)
270
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
271
286
  end
272
287
 
273
288
  HTTPI::REQUEST_METHODS.each do |method|
@@ -287,7 +302,8 @@ describe HTTPI do
287
302
  :net_http_persistent => lambda { Net::HTTP::Persistent },
288
303
  :em_http => lambda { EventMachine::HttpConnection },
289
304
  :rack => lambda { Rack::MockRequest },
290
- :excon => lambda { Excon::Connection }
305
+ :excon => lambda { Excon::Connection },
306
+ :http => lambda { ::HTTP::Client }
291
307
  }
292
308
 
293
309
  context "using #{adapter}" do
@@ -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"))
@@ -33,6 +33,17 @@ describe HTTPI::Adapter::Curb do
33
33
  expect(response.headers["Set-Cookie"]).to eq(cookies)
34
34
  end
35
35
 
36
+ it "it supports read timeout" do
37
+ require "curb"
38
+
39
+ request = HTTPI::Request.new(@server.url + "timeout")
40
+ request.read_timeout = 0.5 # seconds
41
+
42
+ expect do
43
+ HTTPI.get(request, adapter)
44
+ end.to raise_exception(Curl::Err::TimeoutError)
45
+ end
46
+
36
47
  it "executes GET requests" do
37
48
  response = HTTPI.get(@server.url, adapter)
38
49
  expect(response.body).to eq("get")
@@ -42,6 +42,23 @@ describe HTTPI::Adapter::EmHttpRequest do
42
42
  expect(response.headers["Set-Cookie"]).to eq(cookies)
43
43
  end
44
44
 
45
+ if RUBY_PLATFORM =~ /java/
46
+ pending <<-MSG
47
+ It seems like JRuby is missing support for inactivity timeout! See related issues on GitHub:
48
+ - https://github.com/eventmachine/eventmachine/issues/155
49
+ - https://github.com/eventmachine/eventmachine/pull/312
50
+ MSG
51
+ else
52
+ it "it supports read timeout" do
53
+ request = HTTPI::Request.new(@server.url + "timeout")
54
+ request.read_timeout = 0.5 # seconds
55
+
56
+ expect do
57
+ HTTPI.get(request, adapter)
58
+ end.to raise_exception(HTTPI::TimeoutError)
59
+ end
60
+ end
61
+
45
62
  it "executes GET requests" do
46
63
  response = HTTPI.get(@server.url, adapter)
47
64
  expect(response.body).to eq("get")
@@ -0,0 +1,165 @@
1
+ require "spec_helper"
2
+ require "integration/support/server"
3
+
4
+ describe HTTPI::Adapter::HTTPClient do
5
+
6
+ subject(:adapter) { :excon }
7
+
8
+ context "http requests" do
9
+ before :all do
10
+ @server = IntegrationServer.run
11
+ end
12
+
13
+ after :all do
14
+ @server.stop
15
+ end
16
+
17
+ it "sends and receives HTTP headers" do
18
+ request = HTTPI::Request.new(@server.url + "x-header")
19
+ request.headers["X-Header"] = "HTTPI"
20
+
21
+ response = HTTPI.get(request, adapter)
22
+ expect(response.body).to include("HTTPI")
23
+ end
24
+
25
+ it "it supports headers with multiple values" do
26
+ request = HTTPI::Request.new(@server.url + "cookies")
27
+
28
+ response = HTTPI.get(request, adapter)
29
+ cookies = ["cookie1=chip1; path=/", "cookie2=chip2; path=/"]
30
+ expect(response.headers["Set-Cookie"]).to eq(cookies)
31
+ end
32
+
33
+ it "it supports read timeout" do
34
+ require "excon"
35
+
36
+ request = HTTPI::Request.new(@server.url + "timeout")
37
+ request.read_timeout = 0.5 # seconds
38
+
39
+ expect do
40
+ HTTPI.get(request, adapter)
41
+ end.to raise_exception(Excon::Error::Timeout)
42
+ end
43
+
44
+ it "executes GET requests" do
45
+ response = HTTPI.get(@server.url, adapter)
46
+ expect(response.body).to eq("get")
47
+ expect(response.headers["Content-Type"]).to eq("text/plain")
48
+ end
49
+
50
+ it "executes POST requests" do
51
+ response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
52
+ expect(response.body).to eq("post")
53
+ expect(response.headers["Content-Type"]).to eq("text/plain")
54
+ end
55
+
56
+ it "executes HEAD requests" do
57
+ response = HTTPI.head(@server.url, adapter)
58
+ expect(response.code).to eq(200)
59
+ expect(response.headers["Content-Type"]).to eq("text/plain")
60
+ end
61
+
62
+ it "executes PUT requests" do
63
+ response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
64
+ expect(response.body).to eq("put")
65
+ expect(response.headers["Content-Type"]).to eq("text/plain")
66
+ end
67
+
68
+ it "executes DELETE requests" do
69
+ response = HTTPI.delete(@server.url, adapter)
70
+ expect(response.body).to eq("delete")
71
+ expect(response.headers["Content-Type"]).to eq("text/plain")
72
+ end
73
+
74
+ it "supports basic authentication" do
75
+ request = HTTPI::Request.new(@server.url + "basic-auth")
76
+ request.auth.basic("admin", "secret")
77
+
78
+ response = HTTPI.get(request, adapter)
79
+ expect(response.body).to eq("basic-auth")
80
+ end
81
+
82
+ it "does not support ntlm authentication" do
83
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
84
+ request.auth.ntlm("tester", "vReqSoafRe5O")
85
+
86
+ expect { HTTPI.get(request, adapter) }.
87
+ to raise_error(HTTPI::NotSupportedError, /does not support NTLM authentication/)
88
+ end
89
+
90
+ it "supports disabling verify mode" do
91
+ request = HTTPI::Request.new(@server.url)
92
+ request.auth.ssl.verify_mode = :none
93
+ adapter_class = HTTPI::Adapter.load(adapter).new(request)
94
+ expect(adapter_class.client.data[:ssl_verify_peer]).to eq(false)
95
+ end
96
+
97
+ it "supports chunked response" do
98
+ request = HTTPI::Request.new(@server.url)
99
+ res = ""
100
+ request.on_body do |body|
101
+ res += body
102
+ end
103
+ response = HTTPI.post(request, adapter)
104
+ expect(res).to eq("post")
105
+ expect(response.body).to eq("")
106
+ end
107
+ end
108
+
109
+ if RUBY_PLATFORM =~ /java/
110
+ pending "Puma Server complains: SSL not supported on JRuby"
111
+ else
112
+ context "https requests" do
113
+ before :all do
114
+ @server = IntegrationServer.run(:ssl => true)
115
+ end
116
+ after :all do
117
+ @server.stop
118
+ end
119
+
120
+ it "raises when no certificate was set up" do
121
+ expect { HTTPI.post(@server.url, "", adapter) }.to raise_error(HTTPI::SSLError)
122
+ end
123
+
124
+ it "works when set up properly" do
125
+ request = HTTPI::Request.new(@server.url)
126
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
127
+
128
+ response = HTTPI.get(request, adapter)
129
+ expect(response.body).to eq("get")
130
+ end
131
+
132
+ it "works with min_version/max_version" do
133
+ request = HTTPI::Request.new(@server.url)
134
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
135
+ request.auth.ssl.min_version = :TLS1_2
136
+ request.auth.ssl.max_version = :TLS1_2
137
+
138
+ response = HTTPI.get(request, adapter)
139
+ expect(response.body).to eq("get")
140
+ end
141
+
142
+ it "works with client cert and key provided as file path" do
143
+ request = HTTPI::Request.new(@server.url)
144
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
145
+ request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
146
+ request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
147
+
148
+ response = HTTPI.get(request, adapter)
149
+ expect(response.body).to eq("get")
150
+ end
151
+
152
+ it "works with client cert and key set directly" do
153
+ request = HTTPI::Request.new(@server.url)
154
+
155
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
156
+ request.auth.ssl.cert = OpenSSL::X509::Certificate.new File.open("spec/fixtures/client_cert.pem").read
157
+ request.auth.ssl.cert_key = OpenSSL::PKey.read File.open("spec/fixtures/client_key.pem").read
158
+
159
+ response = HTTPI.get(request, adapter)
160
+ expect(response.body).to eq("get")
161
+ end
162
+ end
163
+ end
164
+
165
+ end
@@ -0,0 +1,147 @@
1
+ require "spec_helper"
2
+ require "integration/support/server"
3
+
4
+ describe HTTPI::Adapter::HTTP do
5
+
6
+ subject(:adapter) { :http }
7
+
8
+ context "http requests" do
9
+ before :all do
10
+ @server = IntegrationServer.run
11
+ end
12
+
13
+ after :all do
14
+ @server.stop
15
+ end
16
+
17
+ it "sends and receives HTTP headers" do
18
+ request = HTTPI::Request.new(@server.url + "x-header")
19
+ request.headers["X-Header"] = "HTTPI"
20
+
21
+ response = HTTPI.get(request, adapter)
22
+ expect(response.body).to include("HTTPI")
23
+ end
24
+
25
+ it "it supports headers with multiple values" do
26
+ request = HTTPI::Request.new(@server.url + "cookies")
27
+
28
+ response = HTTPI.get(request, adapter)
29
+ cookies = ["cookie1=chip1; path=/", "cookie2=chip2; path=/"]
30
+ expect(response.headers["Set-Cookie"]).to eq(cookies)
31
+ end
32
+
33
+ it "it supports read timeout" do
34
+ require "http"
35
+
36
+ request = HTTPI::Request.new(@server.url + "timeout")
37
+ request.read_timeout = 0.5 # seconds
38
+
39
+ expect do
40
+ HTTPI.get(request, adapter)
41
+ end.to raise_exception(HTTP::TimeoutError)
42
+ end
43
+
44
+
45
+ it "executes GET requests" do
46
+ response = HTTPI.get(@server.url, adapter)
47
+ expect(response.body).to eq("get")
48
+ expect(response.headers["Content-Type"]).to eq("text/plain")
49
+ end
50
+
51
+ it "executes POST requests" do
52
+ response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
53
+ expect(response.body).to eq("post")
54
+ expect(response.headers["Content-Type"]).to eq("text/plain")
55
+ end
56
+
57
+ it "executes HEAD requests" do
58
+ response = HTTPI.head(@server.url, adapter)
59
+ expect(response.code).to eq(200)
60
+ expect(response.headers["Content-Type"]).to eq("text/plain")
61
+ end
62
+
63
+ it "executes PUT requests" do
64
+ response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
65
+ expect(response.body).to eq("put")
66
+ expect(response.headers["Content-Type"]).to eq("text/plain")
67
+ end
68
+
69
+ it "executes DELETE requests" do
70
+ response = HTTPI.delete(@server.url, adapter)
71
+ expect(response.body).to eq("delete")
72
+ expect(response.headers["Content-Type"]).to eq("text/plain")
73
+ end
74
+
75
+ it "supports basic authentication" do
76
+ request = HTTPI::Request.new(@server.url + "basic-auth")
77
+ request.auth.basic("admin", "secret")
78
+
79
+ response = HTTPI.get(request, adapter)
80
+ expect(response.body).to eq("basic-auth")
81
+ end
82
+
83
+ it "does not support digest authentication" do
84
+ request = HTTPI::Request.new(@server.url + "digest-auth")
85
+ request.auth.digest("admin", "secret")
86
+
87
+ expect { HTTPI.get(request, adapter) }.
88
+ to raise_error(HTTPI::NotSupportedError, /does not support HTTP digest authentication/)
89
+ end
90
+
91
+ it "does not support ntlm authentication" do
92
+ request = HTTPI::Request.new(@server.url + "ntlm-auth")
93
+ request.auth.ntlm("tester", "vReqSoafRe5O")
94
+
95
+ expect { HTTPI.get(request, adapter) }.
96
+ to raise_error(HTTPI::NotSupportedError, /does not support NTLM digest authentication/)
97
+ end
98
+
99
+ it "supports chunked response" do
100
+ skip("Needs investigation")
101
+ request = HTTPI::Request.new(@server.url)
102
+ res = ""
103
+ request.on_body do |body|
104
+ res += body
105
+ end
106
+ response = HTTPI.post(request, adapter)
107
+ expect(res).to eq("post")
108
+ expect(response.body).to eq("")
109
+ end
110
+ end
111
+
112
+ if RUBY_PLATFORM =~ /java/
113
+ pending "Puma Server complains: SSL not supported on JRuby"
114
+ else
115
+ context "https requests" do
116
+ before :all do
117
+ @server = IntegrationServer.run(:ssl => true)
118
+ end
119
+ after :all do
120
+ @server.stop
121
+ end
122
+
123
+ it "raises when no certificate was set up" do
124
+ expect { HTTPI.post(@server.url, "", adapter) }.to raise_error(HTTPI::SSLError)
125
+ end
126
+
127
+ it "works when set up properly" do
128
+ request = HTTPI::Request.new(@server.url)
129
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
130
+
131
+ response = HTTPI.get(request, adapter)
132
+ expect(response.body).to eq("get")
133
+ end
134
+
135
+ it "works with min_version/max_version" do
136
+ request = HTTPI::Request.new(@server.url)
137
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
138
+ request.auth.ssl.min_version = :TLS1_2
139
+ request.auth.ssl.max_version = :TLS1_2
140
+
141
+ response = HTTPI.get(request, adapter)
142
+ expect(response.body).to eq("get")
143
+ end
144
+ end
145
+ end
146
+
147
+ end