httpi 2.0.0.rc1 → 2.0.0

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.
@@ -1,4 +1,12 @@
1
- ## 2.0.0.rc1
1
+ ## 2.0.0 (2012-12-16)
2
+
3
+ * Feature: [#66](https://github.com/savonrb/httpi/pull/66) adds a `query` method
4
+ to the request.
5
+
6
+ * Fix: [#68](https://github.com/savonrb/httpi/issues/68) request does not yield
7
+ adapter client.
8
+
9
+ ## 2.0.0.rc1 (2012-11-10)
2
10
 
3
11
  * Feature: [#63](https://github.com/savonrb/httpi/pull/63) adds support for
4
12
  EventMachine::HttpRequest. Additional information at [#40](https://github.com/savonrb/httpi/pull/40).
data/Gemfile CHANGED
@@ -4,7 +4,6 @@ gemspec
4
4
  gem "jruby-openssl", :platforms => :jruby
5
5
 
6
6
  # http clients
7
- gem "mock-server", :git => "https://github.com/djanowski/mock-server.git"
8
7
  gem "httpclient", "~> 2.3", :require => false
9
8
  gem "curb", "~> 0.8", :require => false, :platforms => :ruby
10
9
  gem 'em-http-request', :require => false, :platforms => [:ruby, :jruby]
@@ -10,16 +10,16 @@ Gem::Specification.new do |s|
10
10
  s.email = "me@rubiii.com"
11
11
  s.homepage = "http://github.com/savonrb/#{s.name}"
12
12
  s.summary = "Common interface for Ruby's HTTP libraries"
13
- s.description = "HTTPI provides a common interface for Ruby's HTTP libraries"
13
+ s.description = s.summary
14
14
 
15
15
  s.rubyforge_project = s.name
16
16
 
17
17
  s.add_dependency "rack"
18
18
 
19
- s.add_development_dependency "rake", "~> 0.9"
20
- s.add_development_dependency "rspec", "~> 2.11"
21
- s.add_development_dependency "mocha", "~> 0.12"
22
- s.add_development_dependency "webmock", "~> 1.8"
19
+ s.add_development_dependency "rake", "~> 10.0"
20
+ s.add_development_dependency "rspec", "~> 2.12"
21
+ s.add_development_dependency "mocha", "~> 0.13"
22
+ s.add_development_dependency "puma", ">= 2.0.0.b3"
23
23
 
24
24
  s.files = `git ls-files`.split("\n")
25
25
  s.require_path = "lib"
@@ -85,9 +85,11 @@ module HTTPI
85
85
  class NotSupportedError < Error; end
86
86
  class NotImplementedError < Error; end
87
87
 
88
+ module ConnectionError; end
89
+
88
90
  class SSLError < Error
89
91
  def initialize(message = nil, original = $!)
90
- super(message)
92
+ super(message || original.message)
91
93
  @original = original
92
94
  end
93
95
  attr_reader :original
@@ -96,33 +98,33 @@ module HTTPI
96
98
  class << self
97
99
 
98
100
  # Executes an HTTP GET request.
99
- def get(request, adapter = nil)
101
+ def get(request, adapter = nil, &block)
100
102
  request = Request.new(request) if request.kind_of? String
101
- request(:get, request, adapter)
103
+ request(:get, request, adapter, &block)
102
104
  end
103
105
 
104
106
  # Executes an HTTP POST request.
105
- def post(*args)
107
+ def post(*args, &block)
106
108
  request, adapter = request_and_adapter_from(args)
107
- request(:post, request, adapter)
109
+ request(:post, request, adapter, &block)
108
110
  end
109
111
 
110
112
  # Executes an HTTP HEAD request.
111
- def head(request, adapter = nil)
113
+ def head(request, adapter = nil, &block)
112
114
  request = Request.new(request) if request.kind_of? String
113
- request(:head, request, adapter)
115
+ request(:head, request, adapter, &block)
114
116
  end
115
117
 
116
118
  # Executes an HTTP PUT request.
117
- def put(*args)
119
+ def put(*args, &block)
118
120
  request, adapter = request_and_adapter_from(args)
119
- request(:put, request, adapter)
121
+ request(:put, request, adapter, &block)
120
122
  end
121
123
 
122
124
  # Executes an HTTP DELETE request.
123
- def delete(request, adapter = nil)
125
+ def delete(request, adapter = nil, &block)
124
126
  request = Request.new(request) if request.kind_of? String
125
- request(:delete, request, adapter)
127
+ request(:delete, request, adapter, &block)
126
128
  end
127
129
 
128
130
  # Executes an HTTP request for the given +method+.
@@ -34,6 +34,9 @@ module HTTPI
34
34
  raise SSLError
35
35
  rescue Curl::Err::SSLPeerCertificateError
36
36
  raise SSLError
37
+ rescue Curl::Err::ConnectionFailedError # connection refused
38
+ $!.extend ConnectionError
39
+ raise
37
40
  end
38
41
 
39
42
  private
@@ -26,6 +26,9 @@ module HTTPI
26
26
  respond_with @client.request(method, @request.url, nil, @request.body, @request.headers)
27
27
  rescue OpenSSL::SSL::SSLError
28
28
  raise SSLError
29
+ rescue Errno::ECONNREFUSED # connection refused
30
+ $!.extend ConnectionError
31
+ raise
29
32
  end
30
33
 
31
34
  private
@@ -34,6 +34,9 @@ module HTTPI
34
34
  end
35
35
  rescue OpenSSL::SSL::SSLError
36
36
  raise SSLError
37
+ rescue Errno::ECONNREFUSED # connection refused
38
+ $!.extend ConnectionError
39
+ raise
37
40
  end
38
41
 
39
42
  private
@@ -31,6 +31,21 @@ module HTTPI
31
31
  # Returns the +url+ to access.
32
32
  attr_reader :url
33
33
 
34
+ # Sets the +query+ from +url+. Raises an +ArgumentError+ unless the +url+ is valid.
35
+ def query=(query)
36
+ raise ArgumentError, "Invalid URL: #{self.url}" unless self.url.respond_to?(:query)
37
+ if query.kind_of?(Hash)
38
+ query = Rack::Utils.build_query(query)
39
+ end
40
+ query = query.to_s unless query.is_a?(String)
41
+ self.url.query = query
42
+ end
43
+
44
+ # Returns the +query+ from +url+.
45
+ def query
46
+ self.url.query if self.url.respond_to?(:query)
47
+ end
48
+
34
49
  # Sets the +proxy+ to use. Raises an +ArgumentError+ unless the +proxy+ is valid.
35
50
  def proxy=(proxy)
36
51
  @proxy = normalize_url! proxy
@@ -1,5 +1,5 @@
1
1
  module HTTPI
2
2
 
3
- VERSION = "2.0.0.rc1"
3
+ VERSION = "2.0.0"
4
4
 
5
5
  end
@@ -1,18 +1,92 @@
1
1
  require "spec_helper"
2
- require "httpi/adapter/net_http"
3
- require "httpi/request"
4
-
5
- HTTPI::Adapter.load_adapter(:net_http)
2
+ require "integration/support/server"
6
3
 
7
4
  describe HTTPI::Adapter::NetHTTP do
8
5
 
9
- let(:net_http) { Net::HTTP.any_instance }
10
- let(:request) { HTTPI::Request.new("http://example.com") }
11
- let(:basic_response) { { :body => Fixture.xml, :headers => { "Accept-encoding" => "utf-8" } } }
6
+ subject(:adapter) { :net_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
+ response.body.should include("HTTPI")
23
+ end
24
+
25
+ it "executes GET requests" do
26
+ response = HTTPI.get(@server.url, adapter)
27
+ response.body.should eq("get")
28
+ response.headers["Content-Type"].should eq("text/plain")
29
+ end
30
+
31
+ it "executes POST requests" do
32
+ response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
33
+ response.body.should eq("post")
34
+ response.headers["Content-Type"].should eq("text/plain")
35
+ end
36
+
37
+ it "executes HEAD requests" do
38
+ response = HTTPI.head(@server.url, adapter)
39
+ response.code.should == 200
40
+ response.headers["Content-Type"].should eq("text/plain")
41
+ end
42
+
43
+ it "executes PUT requests" do
44
+ response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
45
+ response.body.should eq("put")
46
+ response.headers["Content-Type"].should eq("text/plain")
47
+ end
48
+
49
+ it "executes DELETE requests" do
50
+ response = HTTPI.delete(@server.url, adapter)
51
+ response.body.should eq("delete")
52
+ response.headers["Content-Type"].should eq("text/plain")
53
+ end
54
+
55
+ it "supports basic authentication" do
56
+ request = HTTPI::Request.new(@server.url + "basic-auth")
57
+ request.auth.basic("admin", "secret")
58
+
59
+ response = HTTPI.get(request, adapter)
60
+ response.body.should eq("basic-auth")
61
+ end
62
+ end
63
+
64
+ # it does not support digest auth
65
+
66
+ context "https requests" do
67
+ before :all do
68
+ @server = IntegrationServer.run(:ssl => true)
69
+ end
70
+
71
+ after :all do
72
+ @server.stop
73
+ end
74
+
75
+ # it does not raise when no certificate was set up
76
+
77
+ it "works when set up properly" do
78
+ request = HTTPI::Request.new(@server.url)
79
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
80
+
81
+ response = HTTPI.get(request, adapter)
82
+ expect(response.body).to eq("get")
83
+ end
84
+ end
85
+
86
+ end
87
+
12
88
 
13
- let(:adapter) {
14
- HTTPI::Adapter::NetHTTP.new(request)
15
- }
89
+ __END__
16
90
 
17
91
  describe "#request(:get)" do
18
92
  it "should return a valid HTTPI::Response" do
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+ require "support/error_helper"
3
+
4
+ describe HTTPI do
5
+ include ErrorHelper
6
+
7
+ context "with :httpclient" do
8
+ it "tags Errno::ECONNREFUSED with HTTPI::ConnectionError" do
9
+ expect_error(Errno::ECONNREFUSED, "Connection refused - connect(2)").to be_tagged_with(HTTPI::ConnectionError)
10
+ end
11
+
12
+ def fake_error(error, message)
13
+ request(:httpclient) { |client| client.expects(:request).raises(error, message) }
14
+ end
15
+ end
16
+
17
+ unless RUBY_PLATFORM =~ /java/
18
+ context "with :curb" do
19
+ it "tags Curl::Err::ConnectionFailedError with HTTPI::ConnectionError" do
20
+ expect_error(Curl::Err::ConnectionFailedError, "Curl::Err::ConnectionFailedError").to be_tagged_with(HTTPI::ConnectionError)
21
+ end
22
+
23
+ def fake_error(error, message)
24
+ request(:curb) { |client| client.expects(:send).raises(error, message) }
25
+ end
26
+ end
27
+ end
28
+
29
+ context "with :net_http" do
30
+ it "tags Errno::ECONNREFUSED with HTTPI::ConnectionError" do
31
+ expect_error(Errno::ECONNREFUSED, "Connection refused - connect(2)").to be_tagged_with(HTTPI::ConnectionError)
32
+ end
33
+
34
+ def fake_error(error, message)
35
+ request(:net_http) { |client| client.expects(:start).raises(error, message) }
36
+ end
37
+ end
38
+
39
+ def request(adapter)
40
+ HTTPI.get("http://example.com", adapter) { |client| yield client }
41
+ end
42
+
43
+ end
@@ -1,5 +1,13 @@
1
1
  require "spec_helper"
2
- require "httpi"
2
+
3
+ # find out why httpi doesn't load these automatically. [dh, 2012-12-15]
4
+ unless RUBY_VERSION < "1.9"
5
+ require "em-synchrony"
6
+ require "em-http-request"
7
+ end
8
+ unless RUBY_PLATFORM =~ /java/
9
+ require "curb"
10
+ end
3
11
 
4
12
  describe HTTPI do
5
13
  let(:client) { HTTPI }
@@ -196,15 +204,19 @@ describe HTTPI do
196
204
  :httpclient => lambda { HTTPClient },
197
205
  :curb => lambda { Curl::Easy },
198
206
  :net_http => lambda { Net::HTTP },
199
- :em_http => lambda { EventMachine::WebMockHttpConnection } # in real life: EventMachine::HttpRequest
207
+ :em_http => lambda { EventMachine::HttpConnection }
200
208
  }
201
209
 
202
210
  context "using #{adapter}" do
203
211
  before { opts[:class].any_instance.expects(:request).with(method) }
204
212
 
205
- it "yields the HTTP client instance used for the request" do
213
+ it "#request yields the HTTP client instance" do
206
214
  expect { |b| client.request(method, request, adapter, &b) }.to yield_with_args(client_class[adapter].call)
207
215
  end
216
+
217
+ it "##{method} yields the HTTP client instance" do
218
+ expect { |b| client.send(method, request, adapter, &b) }.to yield_with_args(client_class[adapter].call)
219
+ end
208
220
  end
209
221
  end
210
222
  end
@@ -43,6 +43,35 @@ describe HTTPI::Request do
43
43
  end
44
44
  end
45
45
 
46
+ describe "#query" do
47
+ it "raises an ArgumentError if url not respond to query" do
48
+ expect { request.query = "q=query" }.to raise_error(ArgumentError)
49
+ end
50
+ it "lets you specify query parameter as String" do
51
+ request.url = "http://example.com"
52
+ request.query = "q=query"
53
+ request.url.to_s.should == "http://example.com?q=query"
54
+ end
55
+ it "lets you specify query parameter as Hash" do
56
+ request.url = "http://example.com"
57
+ request.query = {:q => "query"}
58
+ request.url.to_s.should == "http://example.com?q=query"
59
+ end
60
+ it "getter return nil for invalid url" do
61
+ request.query.should be_nil
62
+ end
63
+ it "getter return String for query parameter as String" do
64
+ request.url = "http://example.com"
65
+ request.query = "q=query"
66
+ request.query.should == "q=query"
67
+ end
68
+ it "getter return String for query parameter as Hash" do
69
+ request.url = "http://example.com"
70
+ request.query = {:q => "query"}
71
+ request.query.should == "q=query"
72
+ end
73
+ end
74
+
46
75
  describe "#proxy" do
47
76
  it "lets you specify the proxy URL to use as a String" do
48
77
  request.proxy = "http://proxy.example.com"
@@ -0,0 +1,99 @@
1
+ require "spec_helper"
2
+ require "integration/support/server"
3
+
4
+ describe HTTPI::Adapter::Curb do
5
+
6
+ # curb is not supported on jruby
7
+ unless RUBY_PLATFORM =~ /java/
8
+
9
+ subject(:adapter) { :curb }
10
+
11
+ context "http requests" do
12
+ before :all do
13
+ @server = IntegrationServer.run
14
+ end
15
+
16
+ after :all do
17
+ @server.stop
18
+ end
19
+
20
+ it "sends and receives HTTP headers" do
21
+ request = HTTPI::Request.new(@server.url + "x-header")
22
+ request.headers["X-Header"] = "HTTPI"
23
+
24
+ response = HTTPI.get(request, adapter)
25
+ response.body.should include("HTTPI")
26
+ end
27
+
28
+ it "executes GET requests" do
29
+ response = HTTPI.get(@server.url, adapter)
30
+ response.body.should eq("get")
31
+ response.headers["Content-Type"].should eq("text/plain")
32
+ end
33
+
34
+ it "executes POST requests" do
35
+ response = HTTPI.post(@server.url, "<some>xml</some>", adapter)
36
+ response.body.should eq("post")
37
+ response.headers["Content-Type"].should eq("text/plain")
38
+ end
39
+
40
+ it "executes HEAD requests" do
41
+ response = HTTPI.head(@server.url, adapter)
42
+ response.code.should == 200
43
+ response.headers["Content-Type"].should eq("text/plain")
44
+ end
45
+
46
+ it "executes PUT requests" do
47
+ response = HTTPI.put(@server.url, "<some>xml</some>", adapter)
48
+ response.body.should eq("put")
49
+ response.headers["Content-Type"].should eq("text/plain")
50
+ end
51
+
52
+ it "executes DELETE requests" do
53
+ response = HTTPI.delete(@server.url, adapter)
54
+ response.body.should eq("delete")
55
+ response.headers["Content-Type"].should eq("text/plain")
56
+ end
57
+
58
+ it "supports basic authentication" do
59
+ request = HTTPI::Request.new(@server.url + "basic-auth")
60
+ request.auth.basic("admin", "secret")
61
+
62
+ response = HTTPI.get(request, adapter)
63
+ response.body.should eq("basic-auth")
64
+ end
65
+
66
+ it "supports digest authentication" do
67
+ request = HTTPI::Request.new(@server.url + "digest-auth")
68
+ request.auth.digest("admin", "secret")
69
+
70
+ response = HTTPI.get(request, adapter)
71
+ response.body.should eq("digest-auth")
72
+ end
73
+ end
74
+
75
+ context "https requests" do
76
+ before :all do
77
+ @server = IntegrationServer.run(:ssl => true)
78
+ end
79
+
80
+ after :all do
81
+ @server.stop
82
+ end
83
+
84
+ it "raises when no certificate was set up" do
85
+ expect { HTTPI.post(@server.url, "", adapter) }.to raise_error(HTTPI::SSLError)
86
+ end
87
+
88
+ it "works when set up properly" do
89
+ request = HTTPI::Request.new(@server.url)
90
+ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file
91
+
92
+ response = HTTPI.get(request, adapter)
93
+ expect(response.body).to eq("get")
94
+ end
95
+ end
96
+
97
+ end
98
+ end
99
+