httpi 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  require "uri"
2
+ require "httpi/auth/config"
2
3
 
3
4
  module HTTPI
4
5
 
@@ -7,17 +8,16 @@ module HTTPI
7
8
  # Represents an HTTP request and contains various methods for customizing that request.
8
9
  class Request
9
10
 
10
- # Request accessor methods.
11
- ACCESSORS = [:url, :proxy, :headers, :body, :open_timeout, :read_timeout]
11
+ # Available attribute writers.
12
+ ATTRIBUTES = [:url, :proxy, :headers, :body, :open_timeout, :read_timeout]
12
13
 
13
- # Request authentication methods.
14
- AUTHENTICATION = [:basic_auth, :digest_auth]
15
-
16
- # Accepts a Hash of +options+ which may contain any number of ACCESSORS and/or
17
- # AUTHENTICATION credentials to set.
18
- def initialize(options = {})
19
- assign_accessors options
20
- assign_authentication options
14
+ # Accepts a Hash of +args+ to mass assign attributes and authentication credentials.
15
+ def initialize(args = {})
16
+ if args.kind_of? String
17
+ self.url = args
18
+ elsif args.kind_of?(Hash) && !args.empty?
19
+ mass_assign args
20
+ end
21
21
  end
22
22
 
23
23
  # Sets the +url+ to access. Raises an +ArgumentError+ unless the +url+ is valid.
@@ -36,6 +36,15 @@ module HTTPI
36
36
  # Returns the +proxy+ to use.
37
37
  attr_reader :proxy
38
38
 
39
+ # Returns whether to use SSL.
40
+ def ssl?
41
+ return @ssl unless @ssl.nil?
42
+ !!(url.to_s =~ /^https/)
43
+ end
44
+
45
+ # Sets whether to use SSL.
46
+ attr_writer :ssl
47
+
39
48
  # Returns a Hash of HTTP headers. Defaults to return an empty Hash.
40
49
  def headers
41
50
  @headers ||= {}
@@ -49,55 +58,30 @@ module HTTPI
49
58
  headers["Accept-Encoding"] = "gzip,deflate"
50
59
  end
51
60
 
52
- attr_accessor :body, :open_timeout, :read_timeout, :auth_type
53
-
54
- # Returns whether any authentication credentials were specified.
55
- def auth?
56
- !!auth_type
57
- end
58
-
59
- # Shortcut method for returning the credentials for the authentication specified.
60
- # Return +nil+ unless any authentication credentials were specified.
61
- def credentials
62
- return unless auth?
63
- send "#{auth_type}_auth"
64
- end
61
+ attr_accessor :body, :open_timeout, :read_timeout
65
62
 
66
- # Sets the HTTP basic auth credentials. Accepts an Array or two arguments for the
67
- # +username+ and +password+. Resets the credentials when +nil+ is passed and returns
68
- # an Array of credentials when no +args+ where given.
69
- def basic_auth(*args)
70
- self.auth_type = :basic
71
- @basic_auth = extract_credentials @basic_auth, args.flatten
63
+ # Returns the <tt>HTTPI::Authentication</tt> object.
64
+ def auth
65
+ @auth ||= Auth::Config.new
72
66
  end
73
67
 
74
- # Sets the HTTP digest auth credentials. Accepts an Array or two arguments for the
75
- # +username+ and +password+. Resets the credentials when +nil+ is passed and returns
76
- # an Array of credentials when no +args+ where given.
77
- def digest_auth(*args)
78
- self.auth_type = :digest
79
- @digest_auth = extract_credentials @digest_auth, args.flatten
68
+ # Returns whether any authentication credentials were specified.
69
+ def auth?
70
+ !!auth.type
80
71
  end
81
72
 
82
- private
83
-
84
- def assign_accessors(options)
85
- ACCESSORS.each { |a| send("#{a}=", options[a]) if options[a] }
86
- end
87
-
88
- def assign_authentication(options)
89
- AUTHENTICATION.each { |c| send(c, options[c]) if options[c] }
73
+ # Expects a Hash of +args+ to assign.
74
+ def mass_assign(args)
75
+ ATTRIBUTES.each { |key| send("#{key}=", args[key]) if args[key] }
90
76
  end
91
77
 
78
+ private
79
+
80
+ # Expects a +url+, validates its validity and returns a +URI+ object.
92
81
  def normalize_url!(url)
93
82
  raise ArgumentError, "Invalid URL: #{url}" unless url.to_s =~ /^http/
94
83
  url.kind_of?(URI) ? url : URI(url)
95
84
  end
96
85
 
97
- def extract_credentials(credentials, args)
98
- return unless args.empty? || args.first
99
- args[1] ? args[0, 2] : credentials
100
- end
101
-
102
86
  end
103
87
  end
@@ -1,5 +1,5 @@
1
1
  module HTTPI
2
2
 
3
- VERSION = "0.5.0"
3
+ VERSION = "0.6.0"
4
4
 
5
5
  end
@@ -0,0 +1,16 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICbTCCAdYCCQDC4v8d04615DANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJE
3
+ RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzEOMAwGA1UEChMF
4
+ aHR0cGkxFDASBgNVBAMTC2V4YW1wbGUuY29tMSIwIAYJKoZIhvcNAQkBFhNleGFt
5
+ cGxlQGV4YW1wbGUuY29tMB4XDTEwMTAxNTE4NTg0N1oXDTExMTAxNTE4NTg0N1ow
6
+ ezELMAkGA1UEBhMCREUxEDAOBgNVBAgTB0hhbWJ1cmcxEDAOBgNVBAcTB0hhbWJ1
7
+ cmcxDjAMBgNVBAoTBWh0dHBpMRQwEgYDVQQDEwtleGFtcGxlLmNvbTEiMCAGCSqG
8
+ SIb3DQEJARYTZXhhbXBsZUBleGFtcGxlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
9
+ jQAwgYkCgYEAvJiaojIFQAbFczXkBmjxpxra9LbQm0VIESFSl8uBSjmG/gmCBwKg
10
+ 8O94P3tAjDNClC+fEqBLE37KH4qe76yw7upgRruP5jQzUEL1yCaVtA/DoqgaCxZy
11
+ 7VhB2A3f71Zw6kQPt3BOME68fnGsTX65x9XAawCGzGmJSk/Z6wvml1MCAwEAATAN
12
+ BgkqhkiG9w0BAQUFAAOBgQCxOyni9LOKf17vUKVG8Y4TBzRYwm8/hlEdVEU3JKG0
13
+ /aCCwIJLHl+z+3L4r81IN3+YKrHilqx9K0emboJbBRQklYsv/AE+J44Bq3llRiro
14
+ 0e5zwH61jb1j+kxhcxoGiiy8R7hYho24ljuMgFGqtK3kZSP/t9tBLLVp+ItWQ6xX
15
+ 5g==
16
+ -----END CERTIFICATE-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXQIBAAKBgQC8mJqiMgVABsVzNeQGaPGnGtr0ttCbRUgRIVKXy4FKOYb+CYIH
3
+ AqDw73g/e0CMM0KUL58SoEsTfsofip7vrLDu6mBGu4/mNDNQQvXIJpW0D8OiqBoL
4
+ FnLtWEHYDd/vVnDqRA+3cE4wTrx+caxNfrnH1cBrAIbMaYlKT9nrC+aXUwIDAQAB
5
+ AoGBAKjrGh1KJg+pwPInA5yGJGMil5h1obRgwmKtcPeKi7u6eOFSDMdQoGwMYKyj
6
+ LTYlt21Yleat8XB9sHW9yAstpq5dU8Id2A4wfbJeaBYpek7u5+QwBENO4UrnulTk
7
+ W0d+jECBVYECn8wCStxfoFcQQRhlGrsOn05379cD8e1odMOJAkEA3o/7CsgXqahG
8
+ 7L1HaWYtKnpFfTS+EQgdGvSahOolByAKTtMA2TUBU1FdlCk+ggWBGorqmWON5Qnm
9
+ 7UDHjOasZQJBANjuPOqa9ubqHccGwHec+72pQz6q5e8f1gf1XPn7EEuXsBzYiMMH
10
+ qEa8zpfF0TmhQ0oWN75Cq709gfVVBfx/bVcCQHan1HN/Ef6FlKqKjxQGQXYwEfQa
11
+ tmpmJP5GAktyeaM+1cAIhp9GvxooeveOtaCkRpxcC48ToIbHrLI4oyrfoHECQQC6
12
+ bAHtmz6TMp5ka2j7Yez1EIC5WiQ/WxyTukgsi5V1YOX35B2jfPEf2SGxTE6BOBSb
13
+ lnxRBPqRpkoIiwiZ9OgBAkBOWKBuHXmXM6wr+0p4KQ/DOeStZiBxUT8rYbX/i1BI
14
+ /9Xo48KNerTx7qoDK+jIslDrilahvcwUz0fuVV7rHy/X
15
+ -----END RSA PRIVATE KEY-----
@@ -24,8 +24,7 @@ describe HTTPI::Adapter::Curb do
24
24
  end
25
25
 
26
26
  it "should return a valid HTTPI::Response" do
27
- request = HTTPI::Request.new :url => "http://example.com"
28
- adapter.get(request).should match_response(:body => Fixture.xml)
27
+ adapter.get(basic_request).should match_response(:body => Fixture.xml)
29
28
  end
30
29
  end
31
30
 
@@ -38,8 +37,7 @@ describe HTTPI::Adapter::Curb do
38
37
  end
39
38
 
40
39
  it "should return a valid HTTPI::Response" do
41
- request = HTTPI::Request.new :url => "http://example.com"
42
- adapter.post(request).should match_response(:body => Fixture.xml)
40
+ adapter.post(basic_request).should match_response(:body => Fixture.xml)
43
41
  end
44
42
  end
45
43
 
@@ -52,8 +50,7 @@ describe HTTPI::Adapter::Curb do
52
50
  end
53
51
 
54
52
  it "should return a valid HTTPI::Response" do
55
- request = HTTPI::Request.new :url => "http://example.com"
56
- adapter.head(request).should match_response(:body => Fixture.xml)
53
+ adapter.head(basic_request).should match_response(:body => Fixture.xml)
57
54
  end
58
55
  end
59
56
 
@@ -66,8 +63,7 @@ describe HTTPI::Adapter::Curb do
66
63
  end
67
64
 
68
65
  it "should return a valid HTTPI::Response" do
69
- request = HTTPI::Request.new :url => "http://example.com"
70
- adapter.put(request).should match_response(:body => Fixture.xml)
66
+ adapter.put(basic_request).should match_response(:body => Fixture.xml)
71
67
  end
72
68
  end
73
69
 
@@ -80,9 +76,139 @@ describe HTTPI::Adapter::Curb do
80
76
  end
81
77
 
82
78
  it "should return a valid HTTPI::Response" do
83
- request = HTTPI::Request.new :url => "http://example.com"
84
- adapter.delete(request).should match_response(:body => "")
79
+ adapter.delete(basic_request).should match_response(:body => "")
85
80
  end
86
81
  end
87
82
 
83
+ describe "settings:" do
84
+ before { curb.stubs(:http_get) }
85
+
86
+ describe "url" do
87
+ it "should always set the request url" do
88
+ curb.expects(:url=).with(basic_request.url.to_s)
89
+ adapter.get(basic_request)
90
+ end
91
+ end
92
+
93
+ describe "proxy_url" do
94
+ it "should not be set if not specified" do
95
+ curb.expects(:proxy_url=).never
96
+ adapter.get(basic_request)
97
+ end
98
+
99
+ it "should be set if specified" do
100
+ request = basic_request { |request| request.proxy = "http://proxy.example.com" }
101
+
102
+ curb.expects(:proxy_url=).with(request.proxy.to_s)
103
+ adapter.get(request)
104
+ end
105
+ end
106
+
107
+ describe "timeout" do
108
+ it "should not be set if not specified" do
109
+ curb.expects(:timeout=).never
110
+ adapter.get(basic_request)
111
+ end
112
+
113
+ it "should be set if specified" do
114
+ request = basic_request { |request| request.read_timeout = 30 }
115
+
116
+ curb.expects(:timeout=).with(30)
117
+ adapter.get(request)
118
+ end
119
+ end
120
+
121
+ describe "connect_timeout" do
122
+ it "should not be set if not specified" do
123
+ curb.expects(:connect_timeout=).never
124
+ adapter.get(basic_request)
125
+ end
126
+
127
+ it "should be set if specified" do
128
+ request = basic_request { |request| request.open_timeout = 30 }
129
+
130
+ curb.expects(:connect_timeout=).with(30)
131
+ adapter.get(request)
132
+ end
133
+ end
134
+
135
+ describe "headers" do
136
+ it "should always be set" do
137
+ curb.expects(:headers=).with({})
138
+ adapter.get(basic_request)
139
+ end
140
+ end
141
+
142
+ describe "verbose" do
143
+ it "should always be set to false" do
144
+ curb.expects(:verbose=).with(false)
145
+ adapter.get(basic_request)
146
+ end
147
+ end
148
+
149
+ describe "http_auth_types" do
150
+ it "should be set to :basic for HTTP basic auth" do
151
+ request = basic_request { |request| request.auth.basic "username", "password" }
152
+
153
+ curb.expects(:http_auth_types=).with(:basic)
154
+ adapter.get(request)
155
+ end
156
+
157
+ it "should be set to :digest for HTTP digest auth" do
158
+ request = basic_request { |request| request.auth.digest "username", "password" }
159
+
160
+ curb.expects(:http_auth_types=).with(:digest)
161
+ adapter.get(request)
162
+ end
163
+ end
164
+
165
+ describe "username and password" do
166
+ it "should be set for HTTP basic auth" do
167
+ request = basic_request { |request| request.auth.basic "username", "password" }
168
+
169
+ curb.expects(:username=).with("username")
170
+ curb.expects(:password=).with("password")
171
+ adapter.get(request)
172
+ end
173
+
174
+ it "should be set for HTTP digest auth" do
175
+ request = basic_request { |request| request.auth.digest "username", "password" }
176
+
177
+ curb.expects(:username=).with("username")
178
+ curb.expects(:password=).with("password")
179
+ adapter.get(request)
180
+ end
181
+ end
182
+
183
+ context "(for SSL client auth)" do
184
+ let(:ssl_auth_request) do
185
+ basic_request do |request|
186
+ request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
187
+ request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
188
+ end
189
+ end
190
+
191
+ it "cert_key, cert and ssl_verify_peer should be set" do
192
+ curb.expects(:cert_key=).with(ssl_auth_request.auth.ssl.cert_key_file)
193
+ curb.expects(:cert=).with(ssl_auth_request.auth.ssl.cert_file)
194
+ curb.expects(:ssl_verify_peer=).with(true)
195
+
196
+ adapter.get(ssl_auth_request)
197
+ end
198
+
199
+ it "should set the cacert if specified" do
200
+ ssl_auth_request.auth.ssl.ca_cert_file = "spec/fixtures/client_cert.pem"
201
+ curb.expects(:cacert=).with(ssl_auth_request.auth.ssl.ca_cert_file)
202
+
203
+ adapter.get(ssl_auth_request)
204
+ end
205
+ end
206
+ end
207
+
208
+ def basic_request
209
+ request = HTTPI::Request.new :url => "http://example.com"
210
+ yield request if block_given?
211
+ request
212
+ end
213
+
88
214
  end
@@ -7,6 +7,7 @@ require "httpclient"
7
7
  describe HTTPI::Adapter::HTTPClient do
8
8
  let(:adapter) { HTTPI::Adapter::HTTPClient.new }
9
9
  let(:httpclient) { HTTPClient.any_instance }
10
+ let(:ssl_config) { HTTPClient::SSLConfig.any_instance }
10
11
 
11
12
  describe ".new" do
12
13
  it "should require the HTTPClient gem" do
@@ -16,63 +17,128 @@ describe HTTPI::Adapter::HTTPClient do
16
17
  end
17
18
 
18
19
  describe "#get" do
19
- before do
20
- @request = HTTPI::Request.new :url => "http://example.com"
21
- response = HTTP::Message.new_response Fixture.xml
22
- httpclient.expects(:get).with(@request.url, nil, @request.headers).returns(response)
23
- end
24
-
25
20
  it "should return a valid HTTPI::Response" do
26
- adapter.get(@request).should match_response(:body => Fixture.xml)
21
+ httpclient.expects(:get).with(basic_request.url, nil, basic_request.headers).returns(http_message)
22
+ adapter.get(basic_request).should match_response(:body => Fixture.xml)
27
23
  end
28
24
  end
29
25
 
30
26
  describe "#post" do
31
- before do
32
- @request = HTTPI::Request.new :url => "http://example.com", :body => Fixture.xml
33
- response = HTTP::Message.new_response Fixture.xml
34
- httpclient.expects(:post).with(@request.url, @request.body, @request.headers).returns(response)
35
- end
36
-
37
27
  it "should return a valid HTTPI::Response" do
38
- adapter.post(@request).should match_response(:body => Fixture.xml)
28
+ request = HTTPI::Request.new :url => "http://example.com", :body => Fixture.xml
29
+ httpclient.expects(:post).with(request.url, request.body, request.headers).returns(http_message)
30
+
31
+ adapter.post(request).should match_response(:body => Fixture.xml)
39
32
  end
40
33
  end
41
34
 
42
35
  describe "#head" do
43
- before do
44
- @request = HTTPI::Request.new :url => "http://example.com"
45
- response = HTTP::Message.new_response Fixture.xml
46
- httpclient.expects(:head).with(@request.url, nil, @request.headers).returns(response)
47
- end
48
-
49
36
  it "should return a valid HTTPI::Response" do
50
- adapter.head(@request).should match_response(:body => Fixture.xml)
37
+ httpclient.expects(:head).with(basic_request.url, nil, basic_request.headers).returns(http_message)
38
+ adapter.head(basic_request).should match_response(:body => Fixture.xml)
51
39
  end
52
40
  end
53
41
 
54
42
  describe "#put" do
55
- before do
56
- @request = HTTPI::Request.new :url => "http://example.com", :body => Fixture.xml
57
- response = HTTP::Message.new_response Fixture.xml
58
- httpclient.expects(:put).with(@request.url, @request.body, @request.headers).returns(response)
43
+ it "should return a valid HTTPI::Response" do
44
+ request = HTTPI::Request.new :url => "http://example.com", :body => Fixture.xml
45
+ httpclient.expects(:put).with(request.url, request.body, request.headers).returns(http_message)
46
+
47
+ adapter.put(request).should match_response(:body => Fixture.xml)
59
48
  end
49
+ end
60
50
 
51
+ describe "#delete" do
61
52
  it "should return a valid HTTPI::Response" do
62
- adapter.put(@request).should match_response(:body => Fixture.xml)
53
+ httpclient.expects(:delete).with(basic_request.url, basic_request.headers).returns(http_message(""))
54
+ adapter.delete(basic_request).should match_response(:body => "")
63
55
  end
64
56
  end
65
57
 
66
- describe "#delete" do
67
- before do
68
- @request = HTTPI::Request.new :url => "http://example.com"
69
- response = HTTP::Message.new_response ""
70
- httpclient.expects(:delete).with(@request.url, @request.headers).returns(response)
58
+ describe "settings:" do
59
+ before { httpclient.stubs(:get).returns(http_message) }
60
+
61
+ describe "proxy" do
62
+ it "should have specs"
71
63
  end
72
64
 
73
- it "should return a valid HTTPI::Response" do
74
- adapter.delete(@request).should match_response(:body => "")
65
+ describe "connect_timeout" do
66
+ it "should not be set if not specified" do
67
+ httpclient.expects(:connect_timeout=).never
68
+ adapter.get(basic_request)
69
+ end
70
+
71
+ it "should be set if specified" do
72
+ request = basic_request { |request| request.open_timeout = 30 }
73
+
74
+ httpclient.expects(:connect_timeout=).with(30)
75
+ adapter.get(request)
76
+ end
75
77
  end
78
+
79
+ describe "receive_timeout" do
80
+ it "should not be set if not specified" do
81
+ httpclient.expects(:receive_timeout=).never
82
+ adapter.get(basic_request)
83
+ end
84
+
85
+ it "should be set if specified" do
86
+ request = basic_request { |request| request.read_timeout = 30 }
87
+
88
+ httpclient.expects(:receive_timeout=).with(30)
89
+ adapter.get(request)
90
+ end
91
+ end
92
+
93
+ describe "set_auth" do
94
+ it "should be set for HTTP basic auth" do
95
+ request = basic_request { |request| request.auth.basic "username", "password" }
96
+
97
+ httpclient.expects(:set_auth).with(request.url, *request.auth.credentials)
98
+ adapter.get(request)
99
+ end
100
+
101
+ it "should be set for HTTP digest auth" do
102
+ request = basic_request { |request| request.auth.digest "username", "password" }
103
+
104
+ httpclient.expects(:set_auth).with(request.url, *request.auth.credentials)
105
+ adapter.get(request)
106
+ end
107
+ end
108
+
109
+ context "(for SSL client auth)" do
110
+ let(:ssl_auth_request) do
111
+ basic_request do |request|
112
+ request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
113
+ request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
114
+ end
115
+ end
116
+
117
+ it "client_cert, client_key and verify_mode should be set" do
118
+ ssl_config.expects(:client_cert=).with(ssl_auth_request.auth.ssl.cert)
119
+ ssl_config.expects(:client_key=).with(ssl_auth_request.auth.ssl.cert_key)
120
+ ssl_config.expects(:verify_mode=).with(ssl_auth_request.auth.ssl.openssl_verify_mode)
121
+
122
+ adapter.get(ssl_auth_request)
123
+ end
124
+
125
+ it "should set the client_ca if specified" do
126
+ ssl_auth_request.auth.ssl.ca_cert_file = "spec/fixtures/client_cert.pem"
127
+ ssl_config.expects(:client_ca=).with(ssl_auth_request.auth.ssl.ca_cert)
128
+
129
+ adapter.get(ssl_auth_request)
130
+ end
131
+ end
132
+ end
133
+
134
+ def http_message(body = Fixture.xml)
135
+ HTTP::Message.new_response body
136
+ end
137
+
138
+ def basic_request
139
+ request = HTTPI::Request.new "http://example.com"
140
+ yield request if block_given?
141
+ request
76
142
  end
77
143
 
78
144
  end