savon 2.11.0 → 2.12.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.
- checksums.yaml +4 -4
- data/.travis.yml +12 -4
- data/CHANGELOG.md +25 -0
- data/Gemfile +1 -7
- data/README.md +18 -5
- data/lib/savon/builder.rb +32 -6
- data/lib/savon/client.rb +1 -2
- data/lib/savon/header.rb +2 -2
- data/lib/savon/log_message.rb +1 -1
- data/lib/savon/mock/expectation.rb +11 -2
- data/lib/savon/operation.rb +9 -2
- data/lib/savon/options.rb +47 -18
- data/lib/savon/qualified_message.rb +28 -27
- data/lib/savon/request.rb +17 -6
- data/lib/savon/response.rb +7 -6
- data/lib/savon/soap_fault.rb +1 -3
- data/lib/savon/version.rb +1 -1
- data/savon.gemspec +2 -3
- data/spec/fixtures/response/no_body.xml +1 -0
- data/spec/fixtures/wsdl/brand.xml +624 -0
- data/spec/integration/centra_spec.rb +7 -6
- data/spec/integration/random_quote_spec.rb +1 -1
- data/spec/integration/stockquote_example_spec.rb +6 -0
- data/spec/integration/support/application.rb +1 -1
- data/spec/savon/builder_spec.rb +13 -4
- data/spec/savon/log_message_spec.rb +7 -1
- data/spec/savon/mock_spec.rb +11 -0
- data/spec/savon/options_spec.rb +54 -5
- data/spec/savon/qualified_message_spec.rb +86 -5
- data/spec/savon/request_spec.rb +52 -8
- data/spec/savon/response_spec.rb +5 -0
- data/spec/savon/soap_fault_spec.rb +5 -0
- data/spec/savon/softlayer_spec.rb +27 -0
- data/spec/support/integration.rb +1 -1
- metadata +12 -23
@@ -2,14 +2,16 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module LogInterceptor
|
4
4
|
@@intercepted_request = ""
|
5
|
-
def self.debug(message)
|
5
|
+
def self.debug(message = nil)
|
6
|
+
message ||= yield if block_given?
|
7
|
+
|
6
8
|
# save only the first XMLly message
|
7
9
|
if message.include? "xml version"
|
8
10
|
@@intercepted_request = message if @@intercepted_request == ""
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
def self.info(message)
|
14
|
+
def self.info(message = nil)
|
13
15
|
end
|
14
16
|
|
15
17
|
def self.get_intercepted_request
|
@@ -27,6 +29,7 @@ describe 'Correct translation of attributes to XML' do
|
|
27
29
|
|
28
30
|
client = Savon.client(
|
29
31
|
:wsdl => "http://mt205.sabameeting.com/CWS/CWS.asmx?WSDL",
|
32
|
+
:log => true,
|
30
33
|
:logger => LogInterceptor
|
31
34
|
)
|
32
35
|
|
@@ -48,14 +51,12 @@ describe 'Correct translation of attributes to XML' do
|
|
48
51
|
|
49
52
|
client = Savon.client(
|
50
53
|
:wsdl => "http://mt205.sabameeting.com/CWS/CWS.asmx?WSDL",
|
54
|
+
:log => true,
|
51
55
|
:logger => LogInterceptor
|
52
56
|
)
|
53
57
|
|
54
58
|
response = nil
|
55
|
-
|
56
|
-
response = call_and_fail_gracefully(client, :add_new_user, :message => { :user => {}, :attributes! => { :user => { :userID => "test" } } })
|
57
|
-
rescue
|
58
|
-
end
|
59
|
+
response = call_and_fail_gracefully(client, :add_new_user, :message => { :user => {}, :attributes! => { :user => { :userID => "test" } } })
|
59
60
|
|
60
61
|
xml_doc = Nokogiri::XML(LogInterceptor.get_intercepted_request)
|
61
62
|
xml_doc.remove_namespaces!
|
@@ -16,7 +16,7 @@ describe 'rpc/encoded binding test' do
|
|
16
16
|
$stderr.puts e.to_hash.inspect
|
17
17
|
f_c = e.to_hash[:fault][:faultstring]
|
18
18
|
expect(f_c).not_to eq('No such operation \'getQuoteRequest\'')
|
19
|
-
expect(f_c).to eq('
|
19
|
+
expect(f_c).to eq('lucee.runtime.exp.DatabaseException: ')
|
20
20
|
pending e
|
21
21
|
end
|
22
22
|
end
|
@@ -19,6 +19,12 @@ describe "Stockquote example" do
|
|
19
19
|
|
20
20
|
cdata = response.body[:get_quote_response][:get_quote_result]
|
21
21
|
|
22
|
+
if cdata == "exception"
|
23
|
+
# Fallback to not fail the specs when the service's API limit is reached,
|
24
|
+
# but to mark the spec as pending instead.
|
25
|
+
pending "Exception on API"
|
26
|
+
end
|
27
|
+
|
22
28
|
nori_options = { :convert_tags_to => lambda { |tag| tag.snakecase.to_sym } }
|
23
29
|
result = Nori.new(nori_options).parse(cdata)
|
24
30
|
|
data/spec/savon/builder_spec.rb
CHANGED
@@ -83,10 +83,19 @@ describe Savon::Builder do
|
|
83
83
|
end
|
84
84
|
|
85
85
|
describe "#wsse_signature" do
|
86
|
-
|
87
|
-
|
88
|
-
let(:
|
89
|
-
let(:
|
86
|
+
fixture_dir = File.join(File.dirname(__FILE__), '..', 'fixtures', 'ssl')
|
87
|
+
|
88
|
+
let(:cert) { File.join(fixture_dir, 'client_cert.pem') }
|
89
|
+
let(:private_key) { File.join(fixture_dir, 'client_key.pem') }
|
90
|
+
let(:signature) do
|
91
|
+
Akami::WSSE::Signature.new(
|
92
|
+
Akami::WSSE::Certs.new(
|
93
|
+
:cert_file => cert,
|
94
|
+
:private_key_file => private_key
|
95
|
+
)
|
96
|
+
)
|
97
|
+
end
|
98
|
+
let(:globals) { Savon::GlobalOptions.new(wsse_signature: signature) }
|
90
99
|
|
91
100
|
subject(:signed_message_nn) {Nokogiri::XML(builder.to_s).remove_namespaces!}
|
92
101
|
subject(:signed_message) {Nokogiri::XML(builder.to_s)}
|
@@ -21,9 +21,15 @@ describe Savon::LogMessage do
|
|
21
21
|
expect(message).to include("\n <body>")
|
22
22
|
end
|
23
23
|
|
24
|
-
it "filters tags in a given message" do
|
24
|
+
it "filters tags in a given message without pretty printing" do
|
25
25
|
message = log_message("<root><password>secret</password></root>", [:password], false).to_s
|
26
26
|
expect(message).to include("<password>***FILTERED***</password>")
|
27
|
+
expect(message).to_not include("\n <password>***FILTERED***</password>") # no pretty printing
|
28
|
+
end
|
29
|
+
|
30
|
+
it "filters tags in a given message with pretty printing" do
|
31
|
+
message = log_message("<root><password>secret</password></root>", [:password], true).to_s
|
32
|
+
expect(message).to include("\n <password>***FILTERED***</password>")
|
27
33
|
end
|
28
34
|
|
29
35
|
it "properly applies Proc filter" do
|
data/spec/savon/mock_spec.rb
CHANGED
@@ -23,6 +23,17 @@ describe "Savon's mock interface" do
|
|
23
23
|
expect(response.http.body).to eq("<fixture/>")
|
24
24
|
end
|
25
25
|
|
26
|
+
it "can verify a request with any parameters and return a fixture response" do
|
27
|
+
message = { :username => "luke", :password => :any }
|
28
|
+
savon.expects(:authenticate).with(:message => message).returns("<fixture/>")
|
29
|
+
|
30
|
+
response = new_client.call(:authenticate) do
|
31
|
+
message(:username => "luke", :password => "secret")
|
32
|
+
end
|
33
|
+
|
34
|
+
expect(response.http.body).to eq("<fixture/>")
|
35
|
+
end
|
36
|
+
|
26
37
|
it "accepts a Hash to specify the response code, headers and body" do
|
27
38
|
soap_fault = Fixture.response(:soap_fault)
|
28
39
|
response = { :code => 500, :headers => { "X-Result" => "invalid" }, :body => soap_fault }
|
data/spec/savon/options_spec.rb
CHANGED
@@ -114,6 +114,15 @@ describe "Options" do
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
+
context "global :host" do
|
118
|
+
it "overrides the WSDL endpoint host" do
|
119
|
+
client = new_client(:wsdl => Fixture.wsdl(:no_message_tag), host: "https://example.com:8080")
|
120
|
+
|
121
|
+
request = client.build_request(:update_orders)
|
122
|
+
expect(request.url.to_s).to eq "https://example.com:8080/webserviceexternal/contracts.asmx"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
117
126
|
context "global :headers" do
|
118
127
|
it "sets the HTTP headers for the next request" do
|
119
128
|
client = new_client(:endpoint => @server.url(:inspect_request), :headers => { "X-Token" => "secret" })
|
@@ -380,18 +389,27 @@ describe "Options" do
|
|
380
389
|
|
381
390
|
context "global :ssl_version" do
|
382
391
|
it "sets the SSL version to use" do
|
383
|
-
HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:
|
392
|
+
HTTPI::Auth::SSL.any_instance.expects(:ssl_version=).with(:TLSv1).twice
|
384
393
|
|
385
|
-
client = new_client(:endpoint => @server.url, :ssl_version => :
|
394
|
+
client = new_client(:endpoint => @server.url, :ssl_version => :TLSv1)
|
386
395
|
client.call(:authenticate)
|
387
396
|
end
|
388
397
|
end
|
389
398
|
|
390
399
|
context "global :ssl_verify_mode" do
|
391
400
|
it "sets the verify mode to use" do
|
392
|
-
HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:
|
401
|
+
HTTPI::Auth::SSL.any_instance.expects(:verify_mode=).with(:peer).twice
|
402
|
+
|
403
|
+
client = new_client(:endpoint => @server.url, :ssl_verify_mode => :peer)
|
404
|
+
client.call(:authenticate)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context "global :ssl_ciphers" do
|
409
|
+
it "sets the ciphers to use" do
|
410
|
+
HTTPI::Auth::SSL.any_instance.expects(:ciphers=).with(:none).twice
|
393
411
|
|
394
|
-
client = new_client(:endpoint => @server.url, :
|
412
|
+
client = new_client(:endpoint => @server.url, :ssl_ciphers => :none)
|
395
413
|
client.call(:authenticate)
|
396
414
|
end
|
397
415
|
end
|
@@ -460,6 +478,26 @@ describe "Options" do
|
|
460
478
|
end
|
461
479
|
end
|
462
480
|
|
481
|
+
context "global :ssl_ca_cert_path" do
|
482
|
+
it "sets the ca cert path to use" do
|
483
|
+
ca_cert_path = "../../fixtures/ssl"
|
484
|
+
HTTPI::Auth::SSL.any_instance.expects(:ca_cert_path=).with(ca_cert_path).twice
|
485
|
+
|
486
|
+
client = new_client(:endpoint => @server.url, :ssl_ca_cert_path => ca_cert_path)
|
487
|
+
client.call(:authenticate)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
context "global :ssl_ca_cert_store" do
|
492
|
+
it "sets the cert store to use" do
|
493
|
+
cert_store = OpenSSL::X509::Store.new
|
494
|
+
HTTPI::Auth::SSL.any_instance.expects(:cert_store=).with(cert_store).twice
|
495
|
+
|
496
|
+
client = new_client(:endpoint => @server.url, :ssl_cert_store => cert_store)
|
497
|
+
client.call(:authenticate)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
463
501
|
context "global :ssl_ca_cert" do
|
464
502
|
it "sets the ca cert file to use" do
|
465
503
|
ca_cert = File.open(File.expand_path("../../fixtures/ssl/client_cert.pem", __FILE__)).read
|
@@ -578,7 +616,7 @@ describe "Options" do
|
|
578
616
|
expect(request).to include("<wsse:Username>#{username}</wsse:Username>")
|
579
617
|
|
580
618
|
# the nonce node
|
581
|
-
expect(request).to match(/<wsse:Nonce.*>.+\n
|
619
|
+
expect(request).to match(/<wsse:Nonce.*>.+\n?<\/wsse:Nonce>/)
|
582
620
|
|
583
621
|
# the created node with a timestamp
|
584
622
|
expect(request).to match(/<wsu:Created>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*<\/wsu:Created>/)
|
@@ -1037,6 +1075,17 @@ describe "Options" do
|
|
1037
1075
|
end
|
1038
1076
|
end
|
1039
1077
|
|
1078
|
+
context "request :headers" do
|
1079
|
+
it "sets headers" do
|
1080
|
+
client = new_client(:endpoint => @server.url(:inspect_request))
|
1081
|
+
|
1082
|
+
response = client.call(:authenticate, :headers => { "X-Token" => "secret" })
|
1083
|
+
x_token = inspect_request(response).x_token
|
1084
|
+
|
1085
|
+
expect(x_token).to eq("secret")
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
|
1040
1089
|
def new_client(globals = {}, &block)
|
1041
1090
|
globals = { :wsdl => Fixture.wsdl(:authentication), :log => false }.merge(globals)
|
1042
1091
|
Savon.client(globals, &block)
|
@@ -4,15 +4,96 @@ module Savon
|
|
4
4
|
describe QualifiedMessage, "#to_hash" do
|
5
5
|
|
6
6
|
context "if a key ends with !" do
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
types = {}
|
7
|
+
let(:used_namespaces) { {} }
|
8
|
+
let(:key_converter) { :camelcase }
|
9
|
+
let(:types) { {} }
|
11
10
|
|
11
|
+
it "restores the ! in a key" do
|
12
12
|
message = described_class.new(types, used_namespaces, key_converter)
|
13
13
|
resulting_hash = message.to_hash({:Metal! => "<Nice/>"}, ["Rock"])
|
14
14
|
|
15
|
-
expect(resulting_hash).to eq({
|
15
|
+
expect(resulting_hash).to eq({ :Metal! => "<Nice/>" })
|
16
|
+
end
|
17
|
+
|
18
|
+
it "properly handles special keys when namespaces are present" do
|
19
|
+
used_namespaces = {
|
20
|
+
%w(tns Foo) => 'ns',
|
21
|
+
%w(tns Foo Bar) => 'ns'
|
22
|
+
}
|
23
|
+
|
24
|
+
hash = {
|
25
|
+
:foo => {
|
26
|
+
:bar => {
|
27
|
+
:zing => 'pow'
|
28
|
+
},
|
29
|
+
:cash => {
|
30
|
+
:@attr1 => 'val1',
|
31
|
+
:content! => 'Chunky Bacon'
|
32
|
+
},
|
33
|
+
:attributes! => {
|
34
|
+
:bar => { :attr2 => 'val2' },
|
35
|
+
},
|
36
|
+
:"self_closing/" => '',
|
37
|
+
:order! => [:cash, :bar, :"self_closing/"]
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
good_result = {
|
42
|
+
"ns:Foo" => {
|
43
|
+
'ns:Bar' => { :zing => "pow" },
|
44
|
+
:cash => {
|
45
|
+
:@attr1 => "val1",
|
46
|
+
:content! => "Chunky Bacon"
|
47
|
+
},
|
48
|
+
:attributes! => {
|
49
|
+
'ns:Bar' => { :attr2 => 'val2' }
|
50
|
+
},
|
51
|
+
:"self_closing/" => '',
|
52
|
+
:order! => [:cash, 'ns:Bar', :"self_closing/"]
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
good_xml = %(<ns:Foo><Cash attr1="val1">Chunky Bacon</Cash><ns:Bar attr2="val2"><Zing>pow</Zing></ns:Bar><SelfClosing/></ns:Foo>)
|
57
|
+
|
58
|
+
message = described_class.new(types, used_namespaces, key_converter)
|
59
|
+
resulting_hash = message.to_hash(hash, ['tns'])
|
60
|
+
xml = Gyoku.xml(resulting_hash, key_converter: key_converter)
|
61
|
+
|
62
|
+
expect(resulting_hash).to eq good_result
|
63
|
+
expect(xml).to eq good_xml
|
64
|
+
end
|
65
|
+
|
66
|
+
it "properly handles boolean false" do
|
67
|
+
used_namespaces = {
|
68
|
+
%w(tns Foo) => 'ns'
|
69
|
+
}
|
70
|
+
|
71
|
+
hash = {
|
72
|
+
:foo => {
|
73
|
+
:falsey => {
|
74
|
+
:@attr1 => false,
|
75
|
+
:content! => false
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
good_result = {
|
81
|
+
"ns:Foo" => {
|
82
|
+
:falsey => {
|
83
|
+
:@attr1 => false,
|
84
|
+
:content! => false
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
good_xml = %(<ns:Foo><Falsey attr1="false">false</Falsey></ns:Foo>)
|
90
|
+
|
91
|
+
message = described_class.new(types, used_namespaces, key_converter)
|
92
|
+
resulting_hash = message.to_hash(hash, ['tns'])
|
93
|
+
xml = Gyoku.xml(resulting_hash, key_converter: key_converter)
|
94
|
+
|
95
|
+
expect(resulting_hash).to eq good_result
|
96
|
+
expect(xml).to eq good_xml
|
16
97
|
end
|
17
98
|
end
|
18
99
|
|
data/spec/savon/request_spec.rb
CHANGED
@@ -5,6 +5,7 @@ describe Savon::WSDLRequest do
|
|
5
5
|
|
6
6
|
let(:globals) { Savon::GlobalOptions.new }
|
7
7
|
let(:http_request) { HTTPI::Request.new }
|
8
|
+
let(:ciphers) { OpenSSL::Cipher.ciphers }
|
8
9
|
|
9
10
|
def new_wsdl_request
|
10
11
|
Savon::WSDLRequest.new(globals, http_request)
|
@@ -16,6 +17,20 @@ describe Savon::WSDLRequest do
|
|
16
17
|
expect(wsdl_request.build).to be_an(HTTPI::Request)
|
17
18
|
end
|
18
19
|
|
20
|
+
describe "headers" do
|
21
|
+
it "are set when specified" do
|
22
|
+
globals.headers("Proxy-Authorization" => "Basic auth")
|
23
|
+
configured_http_request = new_wsdl_request.build
|
24
|
+
|
25
|
+
expect(configured_http_request.headers["Proxy-Authorization"]).to eq("Basic auth")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "are not set otherwise" do
|
29
|
+
configured_http_request = new_wsdl_request.build
|
30
|
+
expect(configured_http_request.headers).to_not include("Proxy-Authorization")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
19
34
|
describe "proxy" do
|
20
35
|
it "is set when specified" do
|
21
36
|
globals.proxy("http://proxy.example.com")
|
@@ -60,8 +75,8 @@ describe Savon::WSDLRequest do
|
|
60
75
|
|
61
76
|
describe "ssl version" do
|
62
77
|
it "is set when specified" do
|
63
|
-
globals.ssl_version(:
|
64
|
-
http_request.auth.ssl.expects(:ssl_version=).with(:
|
78
|
+
globals.ssl_version(:TLSv1)
|
79
|
+
http_request.auth.ssl.expects(:ssl_version=).with(:TLSv1)
|
65
80
|
|
66
81
|
new_wsdl_request.build
|
67
82
|
end
|
@@ -74,8 +89,8 @@ describe Savon::WSDLRequest do
|
|
74
89
|
|
75
90
|
describe "ssl verify mode" do
|
76
91
|
it "is set when specified" do
|
77
|
-
globals.ssl_verify_mode(:
|
78
|
-
http_request.auth.ssl.expects(:verify_mode=).with(:
|
92
|
+
globals.ssl_verify_mode(:peer)
|
93
|
+
http_request.auth.ssl.expects(:verify_mode=).with(:peer)
|
79
94
|
|
80
95
|
new_wsdl_request.build
|
81
96
|
end
|
@@ -86,6 +101,20 @@ describe Savon::WSDLRequest do
|
|
86
101
|
end
|
87
102
|
end
|
88
103
|
|
104
|
+
describe "ssl ciphers" do
|
105
|
+
it "is set when specified" do
|
106
|
+
globals.ssl_ciphers(ciphers)
|
107
|
+
http_request.auth.ssl.expects(:ciphers=).with(ciphers)
|
108
|
+
|
109
|
+
new_wsdl_request.build
|
110
|
+
end
|
111
|
+
|
112
|
+
it "is not set otherwise" do
|
113
|
+
http_request.auth.ssl.expects(:ciphers=).never
|
114
|
+
new_wsdl_request.build
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
89
118
|
describe "ssl cert key file" do
|
90
119
|
it "is set when specified" do
|
91
120
|
cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
|
@@ -232,6 +261,7 @@ describe Savon::SOAPRequest do
|
|
232
261
|
|
233
262
|
let(:globals) { Savon::GlobalOptions.new }
|
234
263
|
let(:http_request) { HTTPI::Request.new }
|
264
|
+
let(:ciphers) { OpenSSL::Cipher.ciphers }
|
235
265
|
|
236
266
|
def new_soap_request
|
237
267
|
Savon::SOAPRequest.new(globals, http_request)
|
@@ -364,8 +394,8 @@ describe Savon::SOAPRequest do
|
|
364
394
|
|
365
395
|
describe "ssl version" do
|
366
396
|
it "is set when specified" do
|
367
|
-
globals.ssl_version(:
|
368
|
-
http_request.auth.ssl.expects(:ssl_version=).with(:
|
397
|
+
globals.ssl_version(:TLSv1)
|
398
|
+
http_request.auth.ssl.expects(:ssl_version=).with(:TLSv1)
|
369
399
|
|
370
400
|
new_soap_request.build
|
371
401
|
end
|
@@ -378,8 +408,8 @@ describe Savon::SOAPRequest do
|
|
378
408
|
|
379
409
|
describe "ssl verify mode" do
|
380
410
|
it "is set when specified" do
|
381
|
-
globals.ssl_verify_mode(:
|
382
|
-
http_request.auth.ssl.expects(:verify_mode=).with(:
|
411
|
+
globals.ssl_verify_mode(:peer)
|
412
|
+
http_request.auth.ssl.expects(:verify_mode=).with(:peer)
|
383
413
|
|
384
414
|
new_soap_request.build
|
385
415
|
end
|
@@ -390,6 +420,20 @@ describe Savon::SOAPRequest do
|
|
390
420
|
end
|
391
421
|
end
|
392
422
|
|
423
|
+
describe "ssl ciphers" do
|
424
|
+
it "is set when specified" do
|
425
|
+
globals.ssl_ciphers(ciphers)
|
426
|
+
http_request.auth.ssl.expects(:ciphers=).with(ciphers)
|
427
|
+
|
428
|
+
new_soap_request.build
|
429
|
+
end
|
430
|
+
|
431
|
+
it "is not set otherwise" do
|
432
|
+
http_request.auth.ssl.expects(:ciphers=).never
|
433
|
+
new_soap_request.build
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
393
437
|
describe "ssl cert key file" do
|
394
438
|
it "is set when specified" do
|
395
439
|
cert_key = File.expand_path("../../fixtures/ssl/client_key.pem", __FILE__)
|