httpclient 2.7.2 → 2.9.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 +5 -5
- data/bin/httpclient +2 -0
- data/lib/hexdump.rb +12 -12
- data/lib/httpclient/http.rb +12 -9
- data/lib/httpclient/jruby_ssl_socket.rb +119 -52
- data/lib/httpclient/session.rb +37 -18
- data/lib/httpclient/ssl_config.rb +90 -122
- data/lib/httpclient/ssl_socket.rb +26 -24
- data/lib/httpclient/util.rb +1 -1
- data/lib/httpclient/version.rb +1 -1
- data/lib/httpclient/webagent-cookie.rb +1 -1
- data/lib/httpclient.rb +19 -10
- data/lib/jsonclient.rb +8 -5
- data/lib/oauthclient.rb +1 -0
- data/sample/auth.rb +1 -1
- data/sample/generate_test_keys.rb +99 -0
- data/test/ca-chain.pem +32 -36
- data/test/ca.cert +16 -19
- data/test/ca.key +27 -0
- data/test/ca.srl +1 -0
- data/test/client-pass.key +30 -18
- data/test/client.cert +17 -16
- data/test/client.key +25 -13
- data/test/fixtures/verify.alt.cert +20 -0
- data/test/fixtures/verify.foo.cert +20 -0
- data/test/fixtures/verify.key +27 -0
- data/test/fixtures/verify.localhost.cert +20 -0
- data/test/helper.rb +5 -7
- data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
- data/test/server.cert +16 -15
- data/test/server.key +25 -13
- data/test/subca.cert +16 -17
- data/test/subca.key +27 -0
- data/test/subca.srl +1 -0
- data/test/test_auth.rb +21 -18
- data/test/test_hexdump.rb +1 -2
- data/test/test_http-access2.rb +33 -23
- data/test/test_httpclient.rb +133 -58
- data/test/test_jsonclient.rb +18 -0
- data/test/test_ssl.rb +205 -23
- metadata +32 -9
@@ -36,60 +36,113 @@ class HTTPClient
|
|
36
36
|
# then add_trust_ca for that purpose.
|
37
37
|
class SSLConfig
|
38
38
|
include HTTPClient::Util
|
39
|
-
|
39
|
+
if SSLEnabled
|
40
|
+
include OpenSSL
|
41
|
+
|
42
|
+
if defined? JRUBY_VERSION
|
43
|
+
module ::OpenSSL
|
44
|
+
module X509
|
45
|
+
class Store
|
46
|
+
attr_reader :_httpclient_cert_store_items
|
47
|
+
|
48
|
+
# TODO: use prepend instead when we drop JRuby + 1.9.x support
|
49
|
+
wrapped = {}
|
50
|
+
|
51
|
+
wrapped[:initialize] = instance_method(:initialize)
|
52
|
+
define_method(:initialize) do |*args|
|
53
|
+
wrapped[:initialize].bind(self).call(*args)
|
54
|
+
@_httpclient_cert_store_items = [ENV['SSL_CERT_FILE'] || :default]
|
55
|
+
end
|
56
|
+
|
57
|
+
[:add_cert, :add_file, :add_path].each do |m|
|
58
|
+
wrapped[m] = instance_method(m)
|
59
|
+
define_method(m) do |cert|
|
60
|
+
res = wrapped[m].bind(self).call(cert)
|
61
|
+
@_httpclient_cert_store_items << cert
|
62
|
+
res
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class << self
|
72
|
+
private
|
73
|
+
def attr_config(symbol)
|
74
|
+
name = symbol.to_s
|
75
|
+
ivar_name = "@#{name}"
|
76
|
+
define_method(name) {
|
77
|
+
instance_variable_get(ivar_name)
|
78
|
+
}
|
79
|
+
define_method("#{name}=") { |rhs|
|
80
|
+
if instance_variable_get(ivar_name) != rhs
|
81
|
+
instance_variable_set(ivar_name, rhs)
|
82
|
+
change_notify
|
83
|
+
end
|
84
|
+
}
|
85
|
+
symbol
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
40
89
|
|
41
90
|
CIPHERS_DEFAULT = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
|
42
91
|
|
43
92
|
# Which TLS protocol version (also called method) will be used. Defaults
|
44
|
-
# to :auto which means that OpenSSL decides (In my tests this resulted
|
93
|
+
# to :auto which means that OpenSSL decides (In my tests this resulted
|
45
94
|
# with always the highest available protocol being used).
|
46
95
|
# String name of OpenSSL's SSL version method name: TLSv1_2, TLSv1_1, TLSv1,
|
47
96
|
# SSLv2, SSLv23, SSLv3 or :auto (and nil) to allow version negotiation (default).
|
48
97
|
# See {OpenSSL::SSL::SSLContext::METHODS} for a list of available versions
|
49
98
|
# in your specific Ruby environment.
|
50
|
-
|
99
|
+
attr_config :ssl_version
|
51
100
|
# OpenSSL::X509::Certificate:: certificate for SSL client authentication.
|
52
101
|
# nil by default. (no client authentication)
|
53
|
-
|
102
|
+
attr_config :client_cert
|
54
103
|
# OpenSSL::PKey::PKey:: private key for SSL client authentication.
|
55
104
|
# nil by default. (no client authentication)
|
56
|
-
|
57
|
-
|
105
|
+
attr_config :client_key
|
106
|
+
# OpenSSL::PKey::PKey:: private key pass phrase for client_key.
|
107
|
+
# nil by default. (no pass phrase)
|
108
|
+
attr_config :client_key_pass
|
58
109
|
|
59
110
|
# A number which represents OpenSSL's verify mode. Default value is
|
60
111
|
# OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT.
|
61
|
-
|
112
|
+
attr_config :verify_mode
|
62
113
|
# A number of verify depth. Certification path which length is longer than
|
63
114
|
# this depth is not allowed.
|
64
115
|
# CAUTION: this is OpenSSL specific option and ignored on JRuby.
|
65
|
-
|
116
|
+
attr_config :verify_depth
|
66
117
|
# A callback handler for custom certificate verification. nil by default.
|
67
118
|
# If the handler is set, handler.call is invoked just after general
|
68
119
|
# OpenSSL's verification. handler.call is invoked with 2 arguments,
|
69
120
|
# ok and ctx; ok is a result of general OpenSSL's verification. ctx is a
|
70
121
|
# OpenSSL::X509::StoreContext.
|
71
|
-
|
122
|
+
attr_config :verify_callback
|
72
123
|
# SSL timeout in sec. nil by default.
|
73
|
-
|
124
|
+
attr_config :timeout
|
74
125
|
# A number of OpenSSL's SSL options. Default value is
|
75
126
|
# OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2
|
76
127
|
# CAUTION: this is OpenSSL specific option and ignored on JRuby.
|
77
128
|
# Use ssl_version to specify the TLS version you want to use.
|
78
|
-
|
129
|
+
attr_config :options
|
79
130
|
# A String of OpenSSL's cipher configuration. Default value is
|
80
131
|
# ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH
|
81
132
|
# See ciphers(1) man in OpenSSL for more detail.
|
82
|
-
|
133
|
+
attr_config :ciphers
|
83
134
|
|
84
135
|
# OpenSSL::X509::X509::Store used for verification. You can reset the
|
85
136
|
# store with clear_cert_store and set the new store with cert_store=.
|
86
137
|
attr_reader :cert_store # don't use if you don't know what it is.
|
87
138
|
|
88
139
|
# For server side configuration. Ignore this.
|
89
|
-
|
140
|
+
attr_config :client_ca # :nodoc:
|
90
141
|
|
91
142
|
# These array keeps original files/dirs that was added to @cert_store
|
92
|
-
|
143
|
+
if defined? JRUBY_VERSION
|
144
|
+
def cert_store_items; @cert_store._httpclient_cert_store_items; end
|
145
|
+
end
|
93
146
|
attr_reader :cert_store_crl_items
|
94
147
|
|
95
148
|
# Creates a SSLConfig.
|
@@ -97,9 +150,8 @@ class HTTPClient
|
|
97
150
|
return unless SSLEnabled
|
98
151
|
@client = client
|
99
152
|
@cert_store = X509::Store.new
|
100
|
-
@cert_store_items = [:default]
|
101
153
|
@cert_store_crl_items = []
|
102
|
-
@client_cert = @client_key = @client_ca = nil
|
154
|
+
@client_cert = @client_key = @client_key_pass = @client_ca = nil
|
103
155
|
@verify_mode = SSL::VERIFY_PEER | SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
104
156
|
@verify_depth = nil
|
105
157
|
@verify_callback = nil
|
@@ -117,42 +169,18 @@ class HTTPClient
|
|
117
169
|
@cacerts_loaded = false
|
118
170
|
end
|
119
171
|
|
120
|
-
# Sets SSL version method String. Possible values: "SSLv2" for SSL2,
|
121
|
-
# "SSLv3" for SSL3 and TLS1.x, "SSLv23" for SSL3 with fallback to SSL2.
|
122
|
-
def ssl_version=(ssl_version)
|
123
|
-
@ssl_version = ssl_version
|
124
|
-
change_notify
|
125
|
-
end
|
126
|
-
|
127
|
-
# Sets certificate (OpenSSL::X509::Certificate) for SSL client
|
128
|
-
# authentication.
|
129
|
-
# client_key and client_cert must be a pair.
|
130
|
-
#
|
131
|
-
# Calling this method resets all existing sessions.
|
132
|
-
def client_cert=(client_cert)
|
133
|
-
@client_cert = client_cert
|
134
|
-
change_notify
|
135
|
-
end
|
136
|
-
|
137
|
-
# Sets private key (OpenSSL::PKey::PKey) for SSL client authentication.
|
138
|
-
# client_key and client_cert must be a pair.
|
139
|
-
#
|
140
|
-
# Calling this method resets all existing sessions.
|
141
|
-
def client_key=(client_key)
|
142
|
-
@client_key = client_key
|
143
|
-
change_notify
|
144
|
-
end
|
145
|
-
|
146
172
|
# Sets certificate and private key for SSL client authentication.
|
147
173
|
# cert_file:: must be a filename of PEM/DER formatted file.
|
148
174
|
# key_file:: must be a filename of PEM/DER formatted file. Key must be an
|
149
175
|
# RSA key. If you want to use other PKey algorithm,
|
150
176
|
# use client_key=.
|
151
177
|
#
|
152
|
-
# Calling this method resets all existing sessions.
|
178
|
+
# Calling this method resets all existing sessions if value is changed.
|
153
179
|
def set_client_cert_file(cert_file, key_file, pass = nil)
|
154
|
-
@client_cert
|
155
|
-
|
180
|
+
if (@client_cert != cert_file) || (@client_key != key_file) || (@client_key_pass != pass)
|
181
|
+
@client_cert, @client_key, @client_key_pass = cert_file, key_file, pass
|
182
|
+
change_notify
|
183
|
+
end
|
156
184
|
end
|
157
185
|
|
158
186
|
# Sets OpenSSL's default trusted CA certificates. Generally, OpenSSL is
|
@@ -170,7 +198,6 @@ class HTTPClient
|
|
170
198
|
@cacerts_loaded = true # avoid lazy override
|
171
199
|
@cert_store = X509::Store.new
|
172
200
|
@cert_store.set_default_paths
|
173
|
-
@cert_store_items = [ENV['SSL_CERT_FILE'] || :default]
|
174
201
|
change_notify
|
175
202
|
end
|
176
203
|
|
@@ -181,7 +208,9 @@ class HTTPClient
|
|
181
208
|
def clear_cert_store
|
182
209
|
@cacerts_loaded = true # avoid lazy override
|
183
210
|
@cert_store = X509::Store.new
|
184
|
-
|
211
|
+
if defined? JRUBY_VERSION
|
212
|
+
@cert_store._httpclient_cert_store_items.clear
|
213
|
+
end
|
185
214
|
change_notify
|
186
215
|
end
|
187
216
|
|
@@ -190,10 +219,12 @@ class HTTPClient
|
|
190
219
|
#
|
191
220
|
# Calling this method resets all existing sessions.
|
192
221
|
def cert_store=(cert_store)
|
193
|
-
|
194
|
-
@cert_store
|
195
|
-
|
196
|
-
|
222
|
+
# This is object equality check, since OpenSSL::X509::Store doesn't overload ==
|
223
|
+
if !@cacerts_loaded || (@cert_store != cert_store)
|
224
|
+
@cacerts_loaded = true # avoid lazy override
|
225
|
+
@cert_store = cert_store
|
226
|
+
change_notify
|
227
|
+
end
|
197
228
|
end
|
198
229
|
|
199
230
|
# Sets trust anchor certificate(s) for verification.
|
@@ -209,7 +240,6 @@ class HTTPClient
|
|
209
240
|
end
|
210
241
|
@cacerts_loaded = true # avoid lazy override
|
211
242
|
add_trust_ca_to_store(@cert_store, trust_ca_file_or_hashed_dir)
|
212
|
-
@cert_store_items << trust_ca_file_or_hashed_dir
|
213
243
|
change_notify
|
214
244
|
end
|
215
245
|
alias set_trust_ca add_trust_ca
|
@@ -252,60 +282,8 @@ class HTTPClient
|
|
252
282
|
end
|
253
283
|
alias set_crl add_crl
|
254
284
|
|
255
|
-
|
256
|
-
|
257
|
-
#
|
258
|
-
# Calling this method resets all existing sessions.
|
259
|
-
def verify_mode=(verify_mode)
|
260
|
-
@verify_mode = verify_mode
|
261
|
-
change_notify
|
262
|
-
end
|
263
|
-
|
264
|
-
# Sets verify depth. New value must be a number.
|
265
|
-
#
|
266
|
-
# Calling this method resets all existing sessions.
|
267
|
-
def verify_depth=(verify_depth)
|
268
|
-
@verify_depth = verify_depth
|
269
|
-
change_notify
|
270
|
-
end
|
271
|
-
|
272
|
-
# Sets callback handler for custom certificate verification.
|
273
|
-
# See verify_callback.
|
274
|
-
#
|
275
|
-
# Calling this method resets all existing sessions.
|
276
|
-
def verify_callback=(verify_callback)
|
277
|
-
@verify_callback = verify_callback
|
278
|
-
change_notify
|
279
|
-
end
|
280
|
-
|
281
|
-
# Sets SSL timeout in sec.
|
282
|
-
#
|
283
|
-
# Calling this method resets all existing sessions.
|
284
|
-
def timeout=(timeout)
|
285
|
-
@timeout = timeout
|
286
|
-
change_notify
|
287
|
-
end
|
288
|
-
|
289
|
-
# Sets SSL options. New value must be a combination of # constants
|
290
|
-
# OpenSSL::SSL::OP_*
|
291
|
-
#
|
292
|
-
# Calling this method resets all existing sessions.
|
293
|
-
def options=(options)
|
294
|
-
@options = options
|
295
|
-
change_notify
|
296
|
-
end
|
297
|
-
|
298
|
-
# Sets cipher configuration. New value must be a String.
|
299
|
-
#
|
300
|
-
# Calling this method resets all existing sessions.
|
301
|
-
def ciphers=(ciphers)
|
302
|
-
@ciphers = ciphers
|
303
|
-
change_notify
|
304
|
-
end
|
305
|
-
|
306
|
-
def client_ca=(client_ca) # :nodoc:
|
307
|
-
@client_ca = client_ca
|
308
|
-
change_notify
|
285
|
+
def verify?
|
286
|
+
@verify_mode && (@verify_mode & OpenSSL::SSL::VERIFY_PEER != 0)
|
309
287
|
end
|
310
288
|
|
311
289
|
# interfaces for SSLSocket.
|
@@ -431,6 +409,9 @@ class HTTPClient
|
|
431
409
|
return true
|
432
410
|
end
|
433
411
|
|
412
|
+
if pathlen > 2
|
413
|
+
warn('pathlen > 2') if $DEBUG
|
414
|
+
end
|
434
415
|
return false
|
435
416
|
end
|
436
417
|
|
@@ -441,23 +422,10 @@ class HTTPClient
|
|
441
422
|
nil
|
442
423
|
end
|
443
424
|
|
444
|
-
# Use
|
445
|
-
# CVE-2015-1793 requires: OpenSSL >= 1.0.2d or OpenSSL >= 1.0.1p
|
446
|
-
# OpenSSL before 1.0.1 does not have CVE-2015-1793 problem
|
425
|
+
# Use 2048 bit certs trust anchor
|
447
426
|
def load_cacerts(cert_store)
|
448
|
-
|
449
|
-
|
450
|
-
(ver.start_with?('OpenSSL ') && ver >= 'OpenSSL 1.0.2d') || defined?(JRuby)
|
451
|
-
filename = 'cacert.pem'
|
452
|
-
else
|
453
|
-
filename = 'cacert1024.pem'
|
454
|
-
end
|
455
|
-
file = File.join(File.dirname(__FILE__), filename)
|
456
|
-
unless defined?(JRuby)
|
457
|
-
# JRuby uses @cert_store_items
|
458
|
-
add_trust_ca_to_store(cert_store, file)
|
459
|
-
end
|
460
|
-
@cert_store_items << file
|
427
|
+
file = File.join(File.dirname(__FILE__), 'cacert.pem')
|
428
|
+
add_trust_ca_to_store(cert_store, file)
|
461
429
|
end
|
462
430
|
end
|
463
431
|
|
@@ -14,43 +14,31 @@ class HTTPClient
|
|
14
14
|
# Wraps up OpenSSL::SSL::SSLSocket and offers debugging features.
|
15
15
|
class SSLSocket
|
16
16
|
def self.create_socket(session)
|
17
|
+
opts = {
|
18
|
+
:debug_dev => session.debug_dev
|
19
|
+
}
|
17
20
|
site = session.proxy || session.dest
|
18
21
|
socket = session.create_socket(site.host, site.port)
|
19
22
|
begin
|
20
23
|
if session.proxy
|
21
24
|
session.connect_ssl_proxy(socket, Util.urify(session.dest.to_s))
|
22
25
|
end
|
23
|
-
|
24
|
-
ssl_socket.ssl_connect(session.dest.host)
|
25
|
-
ssl_socket
|
26
|
+
new(socket, session.dest, session.ssl_config, opts)
|
26
27
|
rescue
|
27
28
|
socket.close
|
28
29
|
raise
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
32
|
-
def initialize(socket,
|
33
|
+
def initialize(socket, dest, config, opts = {})
|
33
34
|
unless SSLEnabled
|
34
35
|
raise ConfigurationError.new('Ruby/OpenSSL module is required')
|
35
36
|
end
|
36
37
|
@socket = socket
|
37
|
-
@
|
38
|
+
@config = config
|
38
39
|
@ssl_socket = create_openssl_socket(@socket)
|
39
|
-
@debug_dev = debug_dev
|
40
|
-
|
41
|
-
|
42
|
-
def ssl_connect(hostname = nil)
|
43
|
-
if hostname && @ssl_socket.respond_to?(:hostname=)
|
44
|
-
@ssl_socket.hostname = hostname
|
45
|
-
end
|
46
|
-
@ssl_socket.connect
|
47
|
-
if $DEBUG
|
48
|
-
if @ssl_socket.respond_to?(:ssl_version)
|
49
|
-
warn("Protocol version: #{@ssl_socket.ssl_version}")
|
50
|
-
end
|
51
|
-
warn("Cipher: #{@ssl_socket.cipher.inspect}")
|
52
|
-
end
|
53
|
-
post_connection_check(hostname)
|
40
|
+
@debug_dev = opts[:debug_dev]
|
41
|
+
ssl_connect(dest.host)
|
54
42
|
end
|
55
43
|
|
56
44
|
def peer_cert
|
@@ -108,8 +96,22 @@ class HTTPClient
|
|
108
96
|
|
109
97
|
private
|
110
98
|
|
99
|
+
def ssl_connect(hostname = nil)
|
100
|
+
if hostname && @ssl_socket.respond_to?(:hostname=)
|
101
|
+
@ssl_socket.hostname = hostname
|
102
|
+
end
|
103
|
+
@ssl_socket.connect
|
104
|
+
if $DEBUG
|
105
|
+
if @ssl_socket.respond_to?(:ssl_version)
|
106
|
+
warn("Protocol version: #{@ssl_socket.ssl_version}")
|
107
|
+
end
|
108
|
+
warn("Cipher: #{@ssl_socket.cipher.inspect}")
|
109
|
+
end
|
110
|
+
post_connection_check(hostname)
|
111
|
+
end
|
112
|
+
|
111
113
|
def post_connection_check(hostname)
|
112
|
-
verify_mode = @
|
114
|
+
verify_mode = @config.verify_mode || OpenSSL::SSL::VERIFY_NONE
|
113
115
|
if verify_mode == OpenSSL::SSL::VERIFY_NONE
|
114
116
|
return
|
115
117
|
elsif @ssl_socket.peer_cert.nil? and
|
@@ -119,7 +121,7 @@ class HTTPClient
|
|
119
121
|
if @ssl_socket.respond_to?(:post_connection_check) and RUBY_VERSION > "1.8.4"
|
120
122
|
@ssl_socket.post_connection_check(hostname)
|
121
123
|
else
|
122
|
-
@
|
124
|
+
@config.post_connection_check(@ssl_socket.peer_cert, hostname)
|
123
125
|
end
|
124
126
|
end
|
125
127
|
|
@@ -131,11 +133,11 @@ class HTTPClient
|
|
131
133
|
ssl_socket = nil
|
132
134
|
if OpenSSL::SSL.const_defined?("SSLContext")
|
133
135
|
ctx = OpenSSL::SSL::SSLContext.new
|
134
|
-
@
|
136
|
+
@config.set_context(ctx)
|
135
137
|
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx)
|
136
138
|
else
|
137
139
|
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket)
|
138
|
-
@
|
140
|
+
@config.set_context(ssl_socket)
|
139
141
|
end
|
140
142
|
ssl_socket
|
141
143
|
end
|
data/lib/httpclient/util.rb
CHANGED
data/lib/httpclient/version.rb
CHANGED
data/lib/httpclient.rb
CHANGED
@@ -309,7 +309,6 @@ class HTTPClient
|
|
309
309
|
if assignable
|
310
310
|
aname = name + '='
|
311
311
|
define_method(aname) { |rhs|
|
312
|
-
reset_all
|
313
312
|
@session_manager.__send__(aname, rhs)
|
314
313
|
}
|
315
314
|
end
|
@@ -356,6 +355,8 @@ class HTTPClient
|
|
356
355
|
# if your ruby is older than 2005-09-06, do not set socket_sync = false to
|
357
356
|
# avoid an SSL socket blocking bug in openssl/buffering.rb.
|
358
357
|
attr_proxy(:socket_sync, true)
|
358
|
+
# Enables TCP keepalive; no timing settings exist at present
|
359
|
+
attr_proxy(:tcp_keepalive, true)
|
359
360
|
# User-Agent header in HTTP request.
|
360
361
|
attr_proxy(:agent_name, true)
|
361
362
|
# From header in HTTP request.
|
@@ -365,6 +366,9 @@ class HTTPClient
|
|
365
366
|
attr_proxy(:test_loopback_http_response)
|
366
367
|
# Decompress a compressed (with gzip or deflate) content body transparently. false by default.
|
367
368
|
attr_proxy(:transparent_gzip_decompression, true)
|
369
|
+
# Raise BadResponseError if response size does not match with Content-Length header in response. false by default.
|
370
|
+
# TODO: enable by default
|
371
|
+
attr_proxy(:strict_response_size_check, true)
|
368
372
|
# Local socket address. Set HTTPClient#socket_local.host and HTTPClient#socket_local.port to specify local binding hostname and port of TCP socket.
|
369
373
|
attr_proxy(:socket_local, true)
|
370
374
|
|
@@ -749,6 +753,10 @@ class HTTPClient
|
|
749
753
|
request(:patch, uri, new_args, &block)
|
750
754
|
end
|
751
755
|
|
756
|
+
# :call-seq:
|
757
|
+
# post(uri, {query: query, body: body, header: header, follow_redirect: follow_redirect}) -> HTTP::Message
|
758
|
+
# post(uri, body, header, follow_redirect) -> HTTP::Message
|
759
|
+
#
|
752
760
|
# Sends POST request to the specified URL. See request for arguments.
|
753
761
|
# You should not depend on :follow_redirect => true for POST method. It
|
754
762
|
# sends the same POST method to the new location which is prohibited in HTTP spec.
|
@@ -844,13 +852,7 @@ class HTTPClient
|
|
844
852
|
end
|
845
853
|
uri = to_resource_url(uri)
|
846
854
|
if block
|
847
|
-
|
848
|
-
filtered_block = proc { |res, str|
|
849
|
-
block.call(str)
|
850
|
-
}
|
851
|
-
else
|
852
|
-
filtered_block = block
|
853
|
-
end
|
855
|
+
filtered_block = adapt_block(&block)
|
854
856
|
end
|
855
857
|
if follow_redirect
|
856
858
|
follow_redirect(method, uri, query, body, header, &block)
|
@@ -1082,11 +1084,17 @@ private
|
|
1082
1084
|
ENV[name.downcase] || ENV[name.upcase]
|
1083
1085
|
end
|
1084
1086
|
|
1087
|
+
def adapt_block(&block)
|
1088
|
+
return block if block.arity == 2
|
1089
|
+
proc { |r, str| block.call(str) }
|
1090
|
+
end
|
1091
|
+
|
1085
1092
|
def follow_redirect(method, uri, query, body, header, &block)
|
1086
1093
|
uri = to_resource_url(uri)
|
1087
1094
|
if block
|
1095
|
+
b = adapt_block(&block)
|
1088
1096
|
filtered_block = proc { |r, str|
|
1089
|
-
|
1097
|
+
b.call(r, str) if r.ok?
|
1090
1098
|
}
|
1091
1099
|
end
|
1092
1100
|
if HTTP::Message.file?(body)
|
@@ -1232,7 +1240,7 @@ private
|
|
1232
1240
|
conn.push(res)
|
1233
1241
|
return res
|
1234
1242
|
end
|
1235
|
-
content = block ? nil : ''
|
1243
|
+
content = block ? nil : ''.dup
|
1236
1244
|
res = HTTP::Message.new_response(content, req.header)
|
1237
1245
|
@debug_dev << "= Request\n\n" if @debug_dev
|
1238
1246
|
sess = @session_manager.query(req, proxy)
|
@@ -1270,6 +1278,7 @@ private
|
|
1270
1278
|
return
|
1271
1279
|
end
|
1272
1280
|
piper, pipew = IO.pipe
|
1281
|
+
pipew.binmode
|
1273
1282
|
res = HTTP::Message.new_response(piper, req.header)
|
1274
1283
|
@debug_dev << "= Request\n\n" if @debug_dev
|
1275
1284
|
sess = @session_manager.query(req, proxy)
|
data/lib/jsonclient.rb
CHANGED
@@ -2,7 +2,7 @@ require 'httpclient'
|
|
2
2
|
require 'json'
|
3
3
|
|
4
4
|
# JSONClient auto-converts Hash <-> JSON in request and response.
|
5
|
-
# * For POST or PUT request, convert Hash body to JSON String with 'application/json; charset=utf-8' header.
|
5
|
+
# * For PATCH, POST or PUT request, convert Hash body to JSON String with 'application/json; charset=utf-8' header.
|
6
6
|
# * For response, convert JSON String to Hash when content-type is '(application|text)/(x-)?json'
|
7
7
|
class JSONClient < HTTPClient
|
8
8
|
CONTENT_TYPE_JSON_REGEX = /(application|text)\/(x-)?json/i
|
@@ -17,6 +17,10 @@ class JSONClient < HTTPClient
|
|
17
17
|
@content_type_json_response_regex = CONTENT_TYPE_JSON_REGEX
|
18
18
|
end
|
19
19
|
|
20
|
+
def patch(uri, *args, &block)
|
21
|
+
request(:patch, uri, argument_to_hash_for_json(args), &block)
|
22
|
+
end
|
23
|
+
|
20
24
|
def post(uri, *args, &block)
|
21
25
|
request(:post, uri, argument_to_hash_for_json(args), &block)
|
22
26
|
end
|
@@ -37,7 +41,7 @@ private
|
|
37
41
|
|
38
42
|
def argument_to_hash_for_json(args)
|
39
43
|
hash = argument_to_hash(args, :body, :header, :follow_redirect)
|
40
|
-
if hash[:body].is_a?(Hash)
|
44
|
+
if hash[:body].is_a?(Hash) || hash[:body].is_a?(Array)
|
41
45
|
hash[:header] = json_header(hash[:header])
|
42
46
|
hash[:body] = JSON.generate(hash[:body])
|
43
47
|
end
|
@@ -47,11 +51,10 @@ private
|
|
47
51
|
def json_header(header)
|
48
52
|
header ||= {}
|
49
53
|
if header.is_a?(Hash)
|
50
|
-
header
|
54
|
+
header.merge('Content-Type' => @content_type_json_request)
|
51
55
|
else
|
52
|
-
header
|
56
|
+
header + [['Content-Type', @content_type_json_request]]
|
53
57
|
end
|
54
|
-
header
|
55
58
|
end
|
56
59
|
|
57
60
|
def wrap_json_response(original)
|
data/lib/oauthclient.rb
CHANGED
data/sample/auth.rb
CHANGED
@@ -7,5 +7,5 @@ c.debug_dev = STDOUT
|
|
7
7
|
#c.set_proxy_auth("admin", "admin")
|
8
8
|
|
9
9
|
# for WWW authentication: supports Basic, Digest and Negotiate.
|
10
|
-
c.set_auth("http://dev.ctor.org/http-access2/", "
|
10
|
+
c.set_auth("http://dev.ctor.org/http-access2/", "username", "password")
|
11
11
|
p c.get("http://dev.ctor.org/http-access2/login")
|