httpi 2.0.0.rc1 → 2.0.0

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