httpi-ntlm 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.autotest +5 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +69 -0
  6. data/Gemfile +9 -0
  7. data/LICENSE +20 -0
  8. data/README.md +223 -0
  9. data/Rakefile +18 -0
  10. data/autotest/discover.rb +1 -0
  11. data/httpi-ntlm.gemspec +27 -0
  12. data/lib/httpi-ntlm.rb +1 -0
  13. data/lib/httpi.rb +198 -0
  14. data/lib/httpi/adapter.rb +67 -0
  15. data/lib/httpi/adapter/curb.rb +125 -0
  16. data/lib/httpi/adapter/httpclient.rb +98 -0
  17. data/lib/httpi/adapter/net_http.rb +117 -0
  18. data/lib/httpi/auth/config.rb +81 -0
  19. data/lib/httpi/auth/ssl.rb +91 -0
  20. data/lib/httpi/dime.rb +56 -0
  21. data/lib/httpi/request.rb +90 -0
  22. data/lib/httpi/response.rb +85 -0
  23. data/lib/httpi/version.rb +5 -0
  24. data/spec/fixtures/attachment.gif +0 -0
  25. data/spec/fixtures/client_cert.pem +16 -0
  26. data/spec/fixtures/client_key.pem +15 -0
  27. data/spec/fixtures/xml.gz +0 -0
  28. data/spec/fixtures/xml.xml +10 -0
  29. data/spec/fixtures/xml_dime.dime +0 -0
  30. data/spec/fixtures/xml_dime.xml +1 -0
  31. data/spec/httpi/adapter/curb_spec.rb +232 -0
  32. data/spec/httpi/adapter/httpclient_spec.rb +164 -0
  33. data/spec/httpi/adapter/net_http_spec.rb +142 -0
  34. data/spec/httpi/adapter_spec.rb +55 -0
  35. data/spec/httpi/auth/config_spec.rb +117 -0
  36. data/spec/httpi/auth/ssl_spec.rb +128 -0
  37. data/spec/httpi/httpi_spec.rb +284 -0
  38. data/spec/httpi/request_spec.rb +135 -0
  39. data/spec/httpi/response_spec.rb +125 -0
  40. data/spec/integration/request_spec.rb +95 -0
  41. data/spec/integration/server.rb +39 -0
  42. data/spec/spec_helper.rb +12 -0
  43. data/spec/support/fixture.rb +27 -0
  44. data/spec/support/matchers.rb +19 -0
  45. metadata +200 -0
@@ -0,0 +1,91 @@
1
+ require "openssl"
2
+
3
+ module HTTPI
4
+ module Auth
5
+
6
+ # = HTTPI::Auth::SSL
7
+ #
8
+ # Provides SSL client authentication.
9
+ class SSL
10
+
11
+ VERIFY_MODES = [:none, :peer, :fail_if_no_peer_cert, :client_once]
12
+ CERT_TYPES = [:pem, :der]
13
+
14
+ # Returns whether SSL configuration is present.
15
+ def present?
16
+ (verify_mode == :none) || (cert && cert_key)
17
+ rescue TypeError, Errno::ENOENT
18
+ false
19
+ end
20
+
21
+ # Accessor for the cert key file to validate SSL certificates.
22
+ attr_accessor :cert_key_file
23
+
24
+ # Accessor for the cert key password to validate SSL certificates.
25
+ attr_accessor :cert_key_password
26
+
27
+ # Accessor for the cert file to validate SSL connections.
28
+ attr_accessor :cert_file
29
+
30
+ # Accessor for the cacert file to validate SSL certificates.
31
+ attr_accessor :ca_cert_file
32
+
33
+ # Returns the cert type to validate SSL certificates PEM|DER.
34
+ def cert_type
35
+ @cert_type ||= :pem
36
+ end
37
+
38
+ # Sets the cert type to validate SSL certificates PEM|DER.
39
+ def cert_type=(type)
40
+ raise ArgumentError, "Invalid SSL cert type: #{type}" unless CERT_TYPES.include? type
41
+ @cert_type = type
42
+ end
43
+
44
+ # Returns the SSL verify mode. Defaults to <tt>:peer</tt>.
45
+ def verify_mode
46
+ @verify_mode ||= :peer
47
+ end
48
+
49
+ # Sets the SSL verify mode. Expects one of <tt>HTTPI::Auth::SSL::VERIFY_MODES</tt>.
50
+ def verify_mode=(mode)
51
+ raise ArgumentError, "Invalid SSL verify mode: #{mode}" unless VERIFY_MODES.include? mode
52
+ @verify_mode = mode
53
+ end
54
+
55
+ # Returns an <tt>OpenSSL::X509::Certificate</tt> for the +cert_file+.
56
+ def cert
57
+ @cert ||= OpenSSL::X509::Certificate.new File.read(cert_file) if cert_file
58
+ end
59
+
60
+ # Sets the +OpenSSL+ certificate.
61
+ attr_writer :cert
62
+
63
+ # Returns an <tt>OpenSSL::X509::Certificate</tt> for the +ca_cert_file+.
64
+ def ca_cert
65
+ @ca_cert ||= OpenSSL::X509::Certificate.new File.read(ca_cert_file)
66
+ end
67
+
68
+ # Sets the +OpenSSL+ ca certificate.
69
+ attr_writer :ca_cert
70
+
71
+ # Returns an <tt>OpenSSL::PKey::RSA</tt> for the +cert_key_file+.
72
+ def cert_key
73
+ @cert_key ||= OpenSSL::PKey::RSA.new(File.read(cert_key_file), cert_key_password) if cert_key_file
74
+ end
75
+
76
+ # Sets the +OpenSSL+ certificate key.
77
+ attr_writer :cert_key
78
+
79
+ # Returns the SSL verify mode as a <tt>OpenSSL::SSL::VERIFY_*</tt> constant.
80
+ def openssl_verify_mode
81
+ case verify_mode
82
+ when :none then OpenSSL::SSL::VERIFY_NONE
83
+ when :peer then OpenSSL::SSL::VERIFY_PEER
84
+ when :fail_if_no_peer_cert then OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
85
+ when :client_once then OpenSSL::SSL::VERIFY_CLIENT_ONCE
86
+ end
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,56 @@
1
+ module HTTPI
2
+ class DimeRecord < Struct.new('DimeRecord', :version, :first, :last, :chunked, :type_format, :options, :id, :type, :data)
3
+ end
4
+
5
+ class Dime < Array
6
+ BINARY = 1
7
+ XML = 2
8
+
9
+ def initialize(body)
10
+ bytes = body.unpack('C*')
11
+
12
+ while bytes.length > 0
13
+ record = DimeRecord.new
14
+
15
+ # Shift out bitfields for the first fields
16
+ byte = bytes.shift
17
+ record.version = (byte >> 3) & 31 # 5 bits DIME format version (always 1)
18
+ record.first = (byte >> 2) & 1 # 1 bit Set if this is the first part in the message
19
+ record.last = (byte >> 1) & 1 # 1 bit Set if this is the last part in the message
20
+ record.chunked = byte & 1 # 1 bit This file is broken into chunked parts
21
+ record.type_format = (bytes.shift >> 4) & 15 # 4 bits Type of file in the part (1 for binary data, 2 for XML)
22
+ # 4 bits Reserved (skipped in the above command)
23
+
24
+ # Fetch big-endian lengths
25
+ lengths = [] # we can't use a hash since the order will be screwed in Ruby 1.8
26
+ lengths << [:options, (bytes.shift << 8) | bytes.shift] # 2 bytes Length of the "options" field
27
+ lengths << [:id, (bytes.shift << 8) | bytes.shift] # 2 bytes Length of the "ID" or "name" field
28
+ lengths << [:type, (bytes.shift << 8) | bytes.shift] # 2 bytes Length of the "type" field
29
+ lengths << [:data, (bytes.shift << 24) | (bytes.shift << 16) | (bytes.shift << 8) | bytes.shift] # 4 bytes Size of the included file
30
+
31
+ # Read in padded data
32
+ lengths.each do |attribute_set|
33
+ attribute, length = attribute_set
34
+ content = bytes.slice!(0, length).pack('C*')
35
+ if attribute == :data && record.type_format == BINARY
36
+ content = StringIO.new(content)
37
+ end
38
+
39
+ record.send "#{attribute.to_s}=", content
40
+
41
+ bytes.slice!(0, 4 - (length & 3)) if (length & 3) != 0
42
+ end
43
+
44
+ self << record
45
+ end
46
+ end
47
+
48
+ def xml_records
49
+ select { |r| r.type_format == XML }
50
+ end
51
+
52
+ def binary_records
53
+ select { |r| r.type_format == BINARY }
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,90 @@
1
+ require "uri"
2
+ require "httpi/auth/config"
3
+ require "rack/utils"
4
+
5
+ module HTTPI
6
+
7
+ # = HTTPI::Request
8
+ #
9
+ # Represents an HTTP request and contains various methods for customizing that request.
10
+ class Request
11
+
12
+ # Available attribute writers.
13
+ ATTRIBUTES = [:url, :proxy, :headers, :body, :open_timeout, :read_timeout]
14
+
15
+ # Accepts a Hash of +args+ to mass assign attributes and authentication credentials.
16
+ def initialize(args = {})
17
+ if args.kind_of? String
18
+ self.url = args
19
+ elsif args.kind_of?(Hash) && !args.empty?
20
+ mass_assign args
21
+ end
22
+ end
23
+
24
+ # Sets the +url+ to access. Raises an +ArgumentError+ unless the +url+ is valid.
25
+ def url=(url)
26
+ @url = normalize_url! url
27
+ end
28
+
29
+ # Returns the +url+ to access.
30
+ attr_reader :url
31
+
32
+ # Sets the +proxy+ to use. Raises an +ArgumentError+ unless the +proxy+ is valid.
33
+ def proxy=(proxy)
34
+ @proxy = normalize_url! proxy
35
+ end
36
+
37
+ # Returns the +proxy+ to use.
38
+ attr_reader :proxy
39
+
40
+ # Returns whether to use SSL.
41
+ def ssl?
42
+ return @ssl unless @ssl.nil?
43
+ !!(url.to_s =~ /^https/)
44
+ end
45
+
46
+ # Sets whether to use SSL.
47
+ attr_writer :ssl
48
+
49
+ # Returns a Hash of HTTP headers. Defaults to return an empty Hash.
50
+ def headers
51
+ @headers ||= Rack::Utils::HeaderHash.new
52
+ end
53
+
54
+ # Sets the Hash of HTTP headers.
55
+ def headers=(headers)
56
+ @headers = Rack::Utils::HeaderHash.new(headers)
57
+ end
58
+
59
+ # Adds a header information to accept gzipped content.
60
+ def gzip
61
+ headers["Accept-Encoding"] = "gzip,deflate"
62
+ end
63
+
64
+ attr_accessor :body, :open_timeout, :read_timeout
65
+
66
+ # Returns the <tt>HTTPI::Authentication</tt> object.
67
+ def auth
68
+ @auth ||= Auth::Config.new
69
+ end
70
+
71
+ # Returns whether any authentication credentials were specified.
72
+ def auth?
73
+ !!auth.type
74
+ end
75
+
76
+ # Expects a Hash of +args+ to assign.
77
+ def mass_assign(args)
78
+ ATTRIBUTES.each { |key| send("#{key}=", args[key]) if args[key] }
79
+ end
80
+
81
+ private
82
+
83
+ # Expects a +url+, validates its validity and returns a +URI+ object.
84
+ def normalize_url!(url)
85
+ raise ArgumentError, "Invalid URL: #{url}" unless url.to_s =~ /^http/
86
+ url.kind_of?(URI) ? url : URI(url)
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,85 @@
1
+ require "zlib"
2
+ require "stringio"
3
+ require "httpi/dime"
4
+ require "rack/utils"
5
+
6
+ module HTTPI
7
+
8
+ # = HTTPI::Response
9
+ #
10
+ # Represents an HTTP response and contains various response details.
11
+ class Response
12
+
13
+ # Range of HTTP response codes considered to be successful.
14
+ SuccessfulResponseCodes = 200..299
15
+
16
+ # Initializer expects an HTTP response +code+, +headers+ and +body+.
17
+ def initialize(code, headers, body)
18
+ self.code = code.to_i
19
+ self.headers = Rack::Utils::HeaderHash.new(headers)
20
+ self.raw_body = body
21
+ end
22
+
23
+ attr_accessor :code, :headers, :raw_body, :attachments
24
+
25
+ # Returns whether the HTTP response is considered successful.
26
+ def error?
27
+ !SuccessfulResponseCodes.include? code.to_i
28
+ end
29
+
30
+ # Returns whether the HTTP response is a multipart response.
31
+ def multipart?
32
+ !!(headers["Content-Type"] =~ /^multipart/i)
33
+ end
34
+
35
+ # Returns any DIME attachments.
36
+ def attachments
37
+ decode_body unless @body
38
+ @attachments ||= []
39
+ end
40
+
41
+ # Returns the HTTP response body.
42
+ def body
43
+ decode_body unless @body
44
+ @body
45
+ end
46
+
47
+ attr_writer :body
48
+
49
+ private
50
+
51
+ def decode_body
52
+ return @body = "" if !raw_body || raw_body.empty?
53
+
54
+ body = gzipped_response? ? decoded_gzip_body : raw_body
55
+ @body = dime_response? ? decoded_dime_body(body) : body
56
+ end
57
+
58
+ # Returns whether the response is gzipped.
59
+ def gzipped_response?
60
+ headers["Content-Encoding"] == "gzip" || raw_body[0..1] == "\x1f\x8b"
61
+ end
62
+
63
+ # Returns whether this is a DIME response.
64
+ def dime_response?
65
+ headers["Content-Type"] == "application/dime"
66
+ end
67
+
68
+ # Returns the gzip decoded response body.
69
+ def decoded_gzip_body
70
+ gzip = Zlib::GzipReader.new StringIO.new(raw_body)
71
+ raise ArgumentError.new "couldn't create gzip reader" unless gzip
72
+ gzip.read
73
+ ensure
74
+ gzip.close if gzip
75
+ end
76
+
77
+ # Returns the DIME decoded response body.
78
+ def decoded_dime_body(body = nil)
79
+ dime = Dime.new(body || raw_body)
80
+ self.attachments = dime.binary_records
81
+ dime.xml_records.first.data
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,5 @@
1
+ module HTTPI
2
+
3
+ VERSION = "0.9.6"
4
+
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-----
Binary file
@@ -0,0 +1,10 @@
1
+ <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ex="http://service.example.com/">
2
+ <soapenv:Header/>
3
+ <soapenv:Body>
4
+ <ex:findUser>
5
+ <request>
6
+ <id>1</id>
7
+ </request>
8
+ </ex:findUser>
9
+ </soapenv:Body>
10
+ </soapenv:Envelope>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><getFileResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><getFileReturn href="#id0"/></getFileResponse><multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns1:FileVW" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://ws"><created xsi:type="xsd:dateTime">2010-10-27T22:00:00.000Z</created><id xsi:type="xsd:int">2181136</id><lastUpdated xsi:type="xsd:dateTime">2010-10-27T22:00:00.000Z</lastUpdated><metadata href="#id1"/><name xsi:type="xsd:string">attachment.gif</name><size xsi:type="xsd:long">80</size><type xsi:type="xsd:string">gif</type><version xsi:type="xsd:long">1</version></multiRef><multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:MetadataVW" xmlns:ns2="http://ws" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"><CR_Latest xsi:type="xsd:boolean">true</CR_Latest><MD5 xsi:type="xsd:string">0xC11FE5DB260A89160FFF4EC26630EEB8</MD5><PSize xsi:type="xsd:string" xsi:nil="true"/><category xsi:type="xsd:string">1</category><completionRate xsi:type="xsd:int">1</completionRate><date xsi:type="xsd:string" xsi:nil="true"/><docNo xsi:type="xsd:string"></docNo><filesId xsi:type="xsd:int">2181136</filesId><init xsi:type="xsd:string"></init><locked xsi:type="xsd:boolean">false</locked><mdate xsi:type="xsd:dateTime">2010-10-28T20:27:00.000Z</mdate><meta1 xsi:type="xsd:string" xsi:nil="true"/><meta10 xsi:type="xsd:string" xsi:nil="true"/><meta11 xsi:type="xsd:string" xsi:nil="true"/><meta12 xsi:type="xsd:string" xsi:nil="true"/><meta13 xsi:type="xsd:string" xsi:nil="true"/><meta14 xsi:type="xsd:string" xsi:nil="true"/><meta15 xsi:type="xsd:string" xsi:nil="true"/><meta16 xsi:type="xsd:string" xsi:nil="true"/><meta17 xsi:type="xsd:string" xsi:nil="true"/><meta18 xsi:type="xsd:string" xsi:nil="true"/><meta19 xsi:type="xsd:string" xsi:nil="true"/><meta2 xsi:type="xsd:string" xsi:nil="true"/><meta20 xsi:type="xsd:string" xsi:nil="true"/><meta3 xsi:type="xsd:string" xsi:nil="true"/><meta4 xsi:type="xsd:string" xsi:nil="true"/><meta5 xsi:type="xsd:string" xsi:nil="true"/><meta6 xsi:type="xsd:string" xsi:nil="true"/><meta7 xsi:type="xsd:string" xsi:nil="true"/><meta8 xsi:type="xsd:string" xsi:nil="true"/><meta9 xsi:type="xsd:string" xsi:nil="true"/><note xsi:type="xsd:string" xsi:nil="true"/><ref xsi:type="xsd:string"></ref><ref2 xsi:type="xsd:string" xsi:nil="true"/><ref3 xsi:type="xsd:string" xsi:nil="true"/><ref4 xsi:type="xsd:string" xsi:nil="true"/><revDate xsi:type="xsd:string"></revDate><revName xsi:type="xsd:string"></revName><scale xsi:type="xsd:string" xsi:nil="true"/><scaleUnit xsi:type="xsd:string" xsi:nil="true"/><size xsi:type="xsd:int">80</size><type xsi:type="xsd:string">Document</type><version xsi:type="xsd:int">1</version></multiRef></soapenv:Body></soapenv:Envelope>
@@ -0,0 +1,232 @@
1
+ require "spec_helper"
2
+ require "httpi/adapter/curb"
3
+ require "httpi/request"
4
+
5
+ # curb does not run on jruby
6
+ unless RUBY_PLATFORM =~ /java/
7
+ require "curb"
8
+
9
+ describe HTTPI::Adapter::Curb do
10
+ let(:adapter) { HTTPI::Adapter::Curb.new }
11
+ let(:curb) { Curl::Easy.any_instance }
12
+
13
+ describe "#get" do
14
+ before do
15
+ curb.expects(:http_get)
16
+ curb.expects(:response_code).returns(200)
17
+ curb.expects(:header_str).returns("Accept-encoding: utf-8")
18
+ curb.expects(:body_str).returns(Fixture.xml)
19
+ end
20
+
21
+ it "returns a valid HTTPI::Response" do
22
+ adapter.get(basic_request).should match_response(:body => Fixture.xml)
23
+ end
24
+ end
25
+
26
+ describe "#post" do
27
+ before do
28
+ curb.expects(:http_post)
29
+ curb.expects(:response_code).returns(200)
30
+ curb.expects(:header_str).returns("Accept-encoding: utf-8")
31
+ curb.expects(:body_str).returns(Fixture.xml)
32
+ end
33
+
34
+ it "returns a valid HTTPI::Response" do
35
+ adapter.post(basic_request).should match_response(:body => Fixture.xml)
36
+ end
37
+ end
38
+
39
+ describe "#post" do
40
+ it "sends the body in the request" do
41
+ curb.expects(:http_post).with('xml=hi&name=123')
42
+ adapter.post(basic_request { |request| request.body = 'xml=hi&name=123' } )
43
+ end
44
+ end
45
+
46
+ describe "#head" do
47
+ before do
48
+ curb.expects(:http_head)
49
+ curb.expects(:response_code).returns(200)
50
+ curb.expects(:header_str).returns("Accept-encoding: utf-8")
51
+ curb.expects(:body_str).returns(Fixture.xml)
52
+ end
53
+
54
+ it "returns a valid HTTPI::Response" do
55
+ adapter.head(basic_request).should match_response(:body => Fixture.xml)
56
+ end
57
+ end
58
+
59
+ describe "#put" do
60
+ before do
61
+ curb.expects(:http_put)
62
+ curb.expects(:response_code).returns(200)
63
+ curb.expects(:header_str).returns("Accept-encoding: utf-8")
64
+ curb.expects(:body_str).returns(Fixture.xml)
65
+ end
66
+
67
+ it "returns a valid HTTPI::Response" do
68
+ adapter.put(basic_request).should match_response(:body => Fixture.xml)
69
+ end
70
+ end
71
+
72
+ describe "#put" do
73
+ it "sends the body in the request" do
74
+ curb.expects(:http_put).with('xml=hi&name=123')
75
+ adapter.put(basic_request { |request| request.body = 'xml=hi&name=123' } )
76
+ end
77
+ end
78
+
79
+ describe "#delete" do
80
+ before do
81
+ curb.expects(:http_delete)
82
+ curb.expects(:response_code).returns(200)
83
+ curb.expects(:header_str).returns("Accept-encoding: utf-8")
84
+ curb.expects(:body_str).returns("")
85
+ end
86
+
87
+ it "returns a valid HTTPI::Response" do
88
+ adapter.delete(basic_request).should match_response(:body => "")
89
+ end
90
+ end
91
+
92
+ describe "settings:" do
93
+ before { curb.stubs(:http_get) }
94
+
95
+ describe "url" do
96
+ it "always sets the request url" do
97
+ curb.expects(:url=).with(basic_request.url.to_s)
98
+ adapter.get(basic_request)
99
+ end
100
+ end
101
+
102
+ describe "proxy_url" do
103
+ it "is not set unless it's specified" do
104
+ curb.expects(:proxy_url=).never
105
+ adapter.get(basic_request)
106
+ end
107
+
108
+ it "is set if specified" do
109
+ request = basic_request { |request| request.proxy = "http://proxy.example.com" }
110
+
111
+ curb.expects(:proxy_url=).with(request.proxy.to_s)
112
+ adapter.get(request)
113
+ end
114
+ end
115
+
116
+ describe "timeout" do
117
+ it "is not set unless it's specified" do
118
+ curb.expects(:timeout=).never
119
+ adapter.get(basic_request)
120
+ end
121
+
122
+ it "is set if specified" do
123
+ request = basic_request { |request| request.read_timeout = 30 }
124
+
125
+ curb.expects(:timeout=).with(30)
126
+ adapter.get(request)
127
+ end
128
+ end
129
+
130
+ describe "connect_timeout" do
131
+ it "is not set unless it's specified" do
132
+ curb.expects(:connect_timeout=).never
133
+ adapter.get(basic_request)
134
+ end
135
+
136
+ it "is set if specified" do
137
+ request = basic_request { |request| request.open_timeout = 30 }
138
+
139
+ curb.expects(:connect_timeout=).with(30)
140
+ adapter.get(request)
141
+ end
142
+ end
143
+
144
+ describe "headers" do
145
+ it "is always set" do
146
+ curb.expects(:headers=).with({})
147
+ adapter.get(basic_request)
148
+ end
149
+ end
150
+
151
+ describe "verbose" do
152
+ it "is always set to false" do
153
+ curb.expects(:verbose=).with(false)
154
+ adapter.get(basic_request)
155
+ end
156
+ end
157
+
158
+ describe "http_auth_types" do
159
+ it "is set to :basic for HTTP basic auth" do
160
+ request = basic_request { |request| request.auth.basic "username", "password" }
161
+
162
+ curb.expects(:http_auth_types=).with(:basic)
163
+ adapter.get(request)
164
+ end
165
+
166
+ it "is set to :digest for HTTP digest auth" do
167
+ request = basic_request { |request| request.auth.digest "username", "password" }
168
+
169
+ curb.expects(:http_auth_types=).with(:digest)
170
+ adapter.get(request)
171
+ end
172
+ end
173
+
174
+ describe "username and password" do
175
+ it "is set for HTTP basic auth" do
176
+ request = basic_request { |request| request.auth.basic "username", "password" }
177
+
178
+ curb.expects(:username=).with("username")
179
+ curb.expects(:password=).with("password")
180
+ adapter.get(request)
181
+ end
182
+
183
+ it "is set for HTTP digest auth" do
184
+ request = basic_request { |request| request.auth.digest "username", "password" }
185
+
186
+ curb.expects(:username=).with("username")
187
+ curb.expects(:password=).with("password")
188
+ adapter.get(request)
189
+ end
190
+ end
191
+
192
+ context "(for SSL client auth)" do
193
+ let(:ssl_auth_request) do
194
+ basic_request do |request|
195
+ request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem"
196
+ request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem"
197
+ end
198
+ end
199
+
200
+ it "cert_key, cert and ssl_verify_peer should be set" do
201
+ curb.expects(:cert_key=).with(ssl_auth_request.auth.ssl.cert_key_file)
202
+ curb.expects(:cert=).with(ssl_auth_request.auth.ssl.cert_file)
203
+ curb.expects(:ssl_verify_peer=).with(true)
204
+ curb.expects(:certtype=).with(ssl_auth_request.auth.ssl.cert_type.to_s.upcase)
205
+
206
+ adapter.get(ssl_auth_request)
207
+ end
208
+
209
+ it "sets the cert_type to DER if specified" do
210
+ ssl_auth_request.auth.ssl.cert_type = :der
211
+ curb.expects(:certtype=).with(:der.to_s.upcase)
212
+
213
+ adapter.get(ssl_auth_request)
214
+ end
215
+
216
+ it "sets the cacert if specified" do
217
+ ssl_auth_request.auth.ssl.ca_cert_file = "spec/fixtures/client_cert.pem"
218
+ curb.expects(:cacert=).with(ssl_auth_request.auth.ssl.ca_cert_file)
219
+
220
+ adapter.get(ssl_auth_request)
221
+ end
222
+ end
223
+ end
224
+
225
+ def basic_request
226
+ request = HTTPI::Request.new :url => "http://example.com"
227
+ yield request if block_given?
228
+ request
229
+ end
230
+
231
+ end
232
+ end