httpi-ntlm 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +5 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +69 -0
- data/Gemfile +9 -0
- data/LICENSE +20 -0
- data/README.md +223 -0
- data/Rakefile +18 -0
- data/autotest/discover.rb +1 -0
- data/httpi-ntlm.gemspec +27 -0
- data/lib/httpi-ntlm.rb +1 -0
- data/lib/httpi.rb +198 -0
- data/lib/httpi/adapter.rb +67 -0
- data/lib/httpi/adapter/curb.rb +125 -0
- data/lib/httpi/adapter/httpclient.rb +98 -0
- data/lib/httpi/adapter/net_http.rb +117 -0
- data/lib/httpi/auth/config.rb +81 -0
- data/lib/httpi/auth/ssl.rb +91 -0
- data/lib/httpi/dime.rb +56 -0
- data/lib/httpi/request.rb +90 -0
- data/lib/httpi/response.rb +85 -0
- data/lib/httpi/version.rb +5 -0
- data/spec/fixtures/attachment.gif +0 -0
- data/spec/fixtures/client_cert.pem +16 -0
- data/spec/fixtures/client_key.pem +15 -0
- data/spec/fixtures/xml.gz +0 -0
- data/spec/fixtures/xml.xml +10 -0
- data/spec/fixtures/xml_dime.dime +0 -0
- data/spec/fixtures/xml_dime.xml +1 -0
- data/spec/httpi/adapter/curb_spec.rb +232 -0
- data/spec/httpi/adapter/httpclient_spec.rb +164 -0
- data/spec/httpi/adapter/net_http_spec.rb +142 -0
- data/spec/httpi/adapter_spec.rb +55 -0
- data/spec/httpi/auth/config_spec.rb +117 -0
- data/spec/httpi/auth/ssl_spec.rb +128 -0
- data/spec/httpi/httpi_spec.rb +284 -0
- data/spec/httpi/request_spec.rb +135 -0
- data/spec/httpi/response_spec.rb +125 -0
- data/spec/integration/request_spec.rb +95 -0
- data/spec/integration/server.rb +39 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/fixture.rb +27 -0
- data/spec/support/matchers.rb +19 -0
- 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
|
data/lib/httpi/dime.rb
ADDED
@@ -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
|
Binary file
|
@@ -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>
|
Binary file
|
@@ -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
|