httpclient 2.4.0 → 2.5.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/README.md +1 -837
- data/lib/httpclient.rb +4 -1
- data/lib/httpclient/auth.rb +14 -50
- data/lib/httpclient/http.rb +4 -4
- data/lib/httpclient/session.rb +3 -3
- data/lib/httpclient/ssl_config.rb +6 -1
- data/lib/httpclient/util.rb +12 -0
- data/lib/httpclient/version.rb +1 -1
- data/sample/github.rb +11 -0
- data/test/test_auth.rb +13 -0
- data/test/test_http-access2.rb +2 -1
- data/test/test_httpclient.rb +1 -1
- data/test/test_ssl.rb +47 -1
- metadata +5 -4
data/lib/httpclient.rb
CHANGED
|
@@ -870,8 +870,11 @@ private
|
|
|
870
870
|
|
|
871
871
|
class KeepAliveDisconnected < StandardError # :nodoc:
|
|
872
872
|
attr_reader :sess
|
|
873
|
-
|
|
873
|
+
attr_reader :cause
|
|
874
|
+
def initialize(sess = nil, cause = nil)
|
|
875
|
+
super("#{self.class.name}: #{cause ? cause.message : nil}")
|
|
874
876
|
@sess = sess
|
|
877
|
+
@cause = cause
|
|
875
878
|
end
|
|
876
879
|
end
|
|
877
880
|
|
data/lib/httpclient/auth.rb
CHANGED
|
@@ -13,27 +13,9 @@ require 'mutex_m'
|
|
|
13
13
|
|
|
14
14
|
class HTTPClient
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
rescue LoadError
|
|
20
|
-
NTLMEnabled = false
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
begin
|
|
24
|
-
require 'win32/sspi'
|
|
25
|
-
SSPIEnabled = true
|
|
26
|
-
rescue LoadError
|
|
27
|
-
SSPIEnabled = false
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
begin
|
|
31
|
-
require 'gssapi'
|
|
32
|
-
GSSAPIEnabled = true
|
|
33
|
-
rescue LoadError
|
|
34
|
-
GSSAPIEnabled = false
|
|
35
|
-
end
|
|
36
|
-
|
|
16
|
+
NTLMEnabled = false
|
|
17
|
+
SSPIEnabled = false
|
|
18
|
+
GSSAPIEnabled = false
|
|
37
19
|
|
|
38
20
|
# Common abstract class for authentication filter.
|
|
39
21
|
#
|
|
@@ -239,7 +221,6 @@ class HTTPClient
|
|
|
239
221
|
def initialize
|
|
240
222
|
super
|
|
241
223
|
@cred = nil
|
|
242
|
-
@set = false
|
|
243
224
|
@auth = {}
|
|
244
225
|
@challenge = {}
|
|
245
226
|
@scheme = "Basic"
|
|
@@ -263,15 +244,12 @@ class HTTPClient
|
|
|
263
244
|
uri = Util.uri_dirname(uri)
|
|
264
245
|
@auth[uri] = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
|
265
246
|
end
|
|
266
|
-
@set = true
|
|
267
247
|
end
|
|
268
248
|
end
|
|
269
249
|
|
|
270
250
|
# have we marked this as set - ie that it's valid to use in this context?
|
|
271
251
|
def set?
|
|
272
|
-
|
|
273
|
-
@set == true
|
|
274
|
-
}
|
|
252
|
+
@cred || @auth.any?
|
|
275
253
|
end
|
|
276
254
|
|
|
277
255
|
# Response handler: returns credential.
|
|
@@ -305,12 +283,10 @@ class HTTPClient
|
|
|
305
283
|
def set(uri, user, passwd)
|
|
306
284
|
synchronize do
|
|
307
285
|
@cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '')
|
|
308
|
-
@set = true
|
|
309
286
|
end
|
|
310
287
|
end
|
|
311
288
|
|
|
312
289
|
def get(req)
|
|
313
|
-
target_uri = req.header.request_uri
|
|
314
290
|
synchronize {
|
|
315
291
|
return nil unless @challenge['challenged']
|
|
316
292
|
@cred
|
|
@@ -339,7 +315,6 @@ class HTTPClient
|
|
|
339
315
|
super
|
|
340
316
|
@auth = {}
|
|
341
317
|
@challenge = {}
|
|
342
|
-
@set = false
|
|
343
318
|
@nonce_count = 0
|
|
344
319
|
@scheme = "Digest"
|
|
345
320
|
end
|
|
@@ -360,15 +335,12 @@ class HTTPClient
|
|
|
360
335
|
uri = Util.uri_dirname(uri)
|
|
361
336
|
@auth[uri] = [user, passwd]
|
|
362
337
|
end
|
|
363
|
-
@set = true
|
|
364
338
|
end
|
|
365
339
|
end
|
|
366
340
|
|
|
367
341
|
# have we marked this as set - ie that it's valid to use in this context?
|
|
368
342
|
def set?
|
|
369
|
-
|
|
370
|
-
@set == true
|
|
371
|
-
}
|
|
343
|
+
@auth.any?
|
|
372
344
|
end
|
|
373
345
|
|
|
374
346
|
# Response handler: returns credential.
|
|
@@ -478,7 +450,6 @@ class HTTPClient
|
|
|
478
450
|
# overrides DigestAuth#set. sets default user name and password. uri is not used.
|
|
479
451
|
def set(uri, user, passwd)
|
|
480
452
|
synchronize do
|
|
481
|
-
@set = true
|
|
482
453
|
@auth = [user, passwd]
|
|
483
454
|
end
|
|
484
455
|
end
|
|
@@ -487,7 +458,6 @@ class HTTPClient
|
|
|
487
458
|
# regardless of target uri if the proxy has required authentication
|
|
488
459
|
# before
|
|
489
460
|
def get(req)
|
|
490
|
-
target_uri = req.header.request_uri
|
|
491
461
|
synchronize {
|
|
492
462
|
param = @challenge
|
|
493
463
|
return nil unless param
|
|
@@ -531,7 +501,6 @@ class HTTPClient
|
|
|
531
501
|
@auth_default = nil
|
|
532
502
|
@challenge = {}
|
|
533
503
|
@scheme = scheme
|
|
534
|
-
@set = false
|
|
535
504
|
@ntlm_opt = {
|
|
536
505
|
:ntlmv2 => true
|
|
537
506
|
}
|
|
@@ -555,21 +524,17 @@ class HTTPClient
|
|
|
555
524
|
else
|
|
556
525
|
@auth_default = [user, passwd]
|
|
557
526
|
end
|
|
558
|
-
@set = true
|
|
559
527
|
end
|
|
560
528
|
end
|
|
561
529
|
|
|
562
530
|
# have we marked this as set - ie that it's valid to use in this context?
|
|
563
531
|
def set?
|
|
564
|
-
|
|
565
|
-
@set == true
|
|
566
|
-
}
|
|
532
|
+
@auth_default || @auth.any?
|
|
567
533
|
end
|
|
568
534
|
|
|
569
535
|
# Response handler: returns credential.
|
|
570
536
|
# See ruby/ntlm for negotiation state transition.
|
|
571
537
|
def get(req)
|
|
572
|
-
return nil unless NTLMEnabled
|
|
573
538
|
target_uri = req.header.request_uri
|
|
574
539
|
synchronize {
|
|
575
540
|
domain_uri, param = @challenge.find { |uri, v|
|
|
@@ -583,6 +548,7 @@ class HTTPClient
|
|
|
583
548
|
user, passwd = @auth_default
|
|
584
549
|
end
|
|
585
550
|
return nil unless user
|
|
551
|
+
Util.try_require('net/ntlm') || return
|
|
586
552
|
domain = nil
|
|
587
553
|
domain, user = user.split("\\") if user.index("\\")
|
|
588
554
|
state = param[:state]
|
|
@@ -606,7 +572,6 @@ class HTTPClient
|
|
|
606
572
|
|
|
607
573
|
# Challenge handler: remember URL and challenge token for response.
|
|
608
574
|
def challenge(uri, param_str)
|
|
609
|
-
return false unless NTLMEnabled
|
|
610
575
|
synchronize {
|
|
611
576
|
if param_str.nil? or @challenge[uri].nil?
|
|
612
577
|
c = @challenge[uri] = {}
|
|
@@ -655,27 +620,27 @@ class HTTPClient
|
|
|
655
620
|
# not supported
|
|
656
621
|
end
|
|
657
622
|
|
|
658
|
-
#
|
|
623
|
+
# Check always (not effective but it works)
|
|
659
624
|
def set?
|
|
660
|
-
|
|
625
|
+
!@challenge.empty?
|
|
661
626
|
end
|
|
662
627
|
|
|
663
628
|
# Response handler: returns credential.
|
|
664
629
|
# See win32/sspi for negotiation state transition.
|
|
665
630
|
def get(req)
|
|
666
|
-
return nil unless SSPIEnabled || GSSAPIEnabled
|
|
667
631
|
target_uri = req.header.request_uri
|
|
668
632
|
synchronize {
|
|
669
633
|
domain_uri, param = @challenge.find { |uri, v|
|
|
670
634
|
Util.uri_part_of(target_uri, uri)
|
|
671
635
|
}
|
|
672
636
|
return nil unless param
|
|
637
|
+
Util.try_require('win32/sspi') || Util.try_require('gssapi') || return
|
|
673
638
|
state = param[:state]
|
|
674
639
|
authenticator = param[:authenticator]
|
|
675
640
|
authphrase = param[:authphrase]
|
|
676
641
|
case state
|
|
677
642
|
when :init
|
|
678
|
-
if
|
|
643
|
+
if defined?(Win32::SSPI)
|
|
679
644
|
authenticator = param[:authenticator] = Win32::SSPI::NegotiateAuth.new
|
|
680
645
|
return authenticator.get_initial_token(@scheme)
|
|
681
646
|
else # use GSSAPI
|
|
@@ -685,7 +650,7 @@ class HTTPClient
|
|
|
685
650
|
end
|
|
686
651
|
when :response
|
|
687
652
|
@challenge.delete(domain_uri)
|
|
688
|
-
if
|
|
653
|
+
if defined?(Win32::SSPI)
|
|
689
654
|
return authenticator.complete_authentication(authphrase)
|
|
690
655
|
else # use GSSAPI
|
|
691
656
|
return authenticator.init_context(authphrase.unpack('m').pop)
|
|
@@ -697,7 +662,6 @@ class HTTPClient
|
|
|
697
662
|
|
|
698
663
|
# Challenge handler: remember URL and challenge token for response.
|
|
699
664
|
def challenge(uri, param_str)
|
|
700
|
-
return false unless SSPIEnabled || GSSAPIEnabled
|
|
701
665
|
synchronize {
|
|
702
666
|
if param_str.nil? or @challenge[uri].nil?
|
|
703
667
|
c = @challenge[uri] = {}
|
|
@@ -825,9 +789,9 @@ class HTTPClient
|
|
|
825
789
|
# not supported
|
|
826
790
|
end
|
|
827
791
|
|
|
828
|
-
#
|
|
792
|
+
# Check always (not effective but it works)
|
|
829
793
|
def set?
|
|
830
|
-
|
|
794
|
+
!@challenge.empty?
|
|
831
795
|
end
|
|
832
796
|
|
|
833
797
|
# Set authentication credential.
|
data/lib/httpclient/http.rb
CHANGED
|
@@ -844,11 +844,11 @@ module HTTP
|
|
|
844
844
|
query.each { |attr, value|
|
|
845
845
|
left = escape(attr.to_s) << '='
|
|
846
846
|
if values = Array.try_convert(value)
|
|
847
|
-
values.each { |
|
|
848
|
-
if
|
|
849
|
-
|
|
847
|
+
values.each { |v|
|
|
848
|
+
if v.respond_to?(:read)
|
|
849
|
+
v = v.read
|
|
850
850
|
end
|
|
851
|
-
pairs.push(left + escape(
|
|
851
|
+
pairs.push(left + escape(v.to_s))
|
|
852
852
|
}
|
|
853
853
|
else
|
|
854
854
|
if value.respond_to?(:read)
|
data/lib/httpclient/session.rb
CHANGED
|
@@ -619,14 +619,14 @@ class HTTPClient
|
|
|
619
619
|
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
|
|
620
620
|
# JRuby can raise IOError instead of ECONNRESET for now
|
|
621
621
|
close
|
|
622
|
-
raise KeepAliveDisconnected.new(self)
|
|
622
|
+
raise KeepAliveDisconnected.new(self, $!)
|
|
623
623
|
rescue HTTPClient::TimeoutError
|
|
624
624
|
close
|
|
625
625
|
raise
|
|
626
626
|
rescue
|
|
627
627
|
close
|
|
628
628
|
if SSLEnabled and $!.is_a?(OpenSSL::SSL::SSLError)
|
|
629
|
-
raise KeepAliveDisconnected.new(self)
|
|
629
|
+
raise KeepAliveDisconnected.new(self, $!)
|
|
630
630
|
else
|
|
631
631
|
raise
|
|
632
632
|
end
|
|
@@ -886,7 +886,7 @@ class HTTPClient
|
|
|
886
886
|
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
|
|
887
887
|
# JRuby can raise IOError instead of ECONNRESET for now
|
|
888
888
|
close
|
|
889
|
-
raise KeepAliveDisconnected.new(self)
|
|
889
|
+
raise KeepAliveDisconnected.new(self, $!)
|
|
890
890
|
end
|
|
891
891
|
if StatusParseRegexp !~ initial_line
|
|
892
892
|
@version = '0.9'
|
|
@@ -90,7 +90,12 @@ class HTTPClient
|
|
|
90
90
|
@dest = nil
|
|
91
91
|
@timeout = nil
|
|
92
92
|
@ssl_version = :auto
|
|
93
|
-
|
|
93
|
+
# Follow ruby-ossl's definition
|
|
94
|
+
@options = OpenSSL::SSL::OP_ALL
|
|
95
|
+
@options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
|
96
|
+
@options |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
|
|
97
|
+
@options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
|
|
98
|
+
@options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
|
|
94
99
|
# OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
|
|
95
100
|
@ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
|
|
96
101
|
@cacerts_loaded = false
|
data/lib/httpclient/util.rb
CHANGED
|
@@ -164,6 +164,18 @@ class HTTPClient
|
|
|
164
164
|
end
|
|
165
165
|
module_function :hash_find_value
|
|
166
166
|
|
|
167
|
+
# Try to require a feature and returns true/false if loaded
|
|
168
|
+
#
|
|
169
|
+
# It returns 'true' for the second require in contrast of the standard
|
|
170
|
+
# require returns false if the feature is already loaded.
|
|
171
|
+
def try_require(feature)
|
|
172
|
+
require feature
|
|
173
|
+
true
|
|
174
|
+
rescue LoadError
|
|
175
|
+
false
|
|
176
|
+
end
|
|
177
|
+
module_function :try_require
|
|
178
|
+
|
|
167
179
|
# Checks if the given URI is https.
|
|
168
180
|
def https?(uri)
|
|
169
181
|
uri.scheme && uri.scheme.downcase == 'https'
|
data/lib/httpclient/version.rb
CHANGED
data/sample/github.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'httpclient'
|
|
2
|
+
|
|
3
|
+
# Obtain Personal access token from https://github.com/settings/applications
|
|
4
|
+
personal_access_token = '03d1c4bc880e51c06215cc77eb075a7856e6a9c7'
|
|
5
|
+
|
|
6
|
+
h = HTTPClient.new
|
|
7
|
+
h.set_auth(nil, personal_access_token, 'x-oauth-basic')
|
|
8
|
+
h.www_auth.basic_auth.challenge('https://api.github.com/')
|
|
9
|
+
h.debug_dev = STDERR
|
|
10
|
+
|
|
11
|
+
puts h.get_content('https://api.github.com/user/repos?type=private')
|
data/test/test_auth.rb
CHANGED
|
@@ -150,6 +150,19 @@ class TestAuth < Test::Unit::TestCase
|
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
+
def test_BASIC_auth_nil_uri
|
|
154
|
+
c = HTTPClient.new
|
|
155
|
+
webrick_backup = @basic_auth.instance_eval { @auth_scheme }
|
|
156
|
+
begin
|
|
157
|
+
@basic_auth.instance_eval { @auth_scheme = "BASIC" }
|
|
158
|
+
c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" }
|
|
159
|
+
c.set_auth(nil, 'admin', 'admin')
|
|
160
|
+
assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth"))
|
|
161
|
+
ensure
|
|
162
|
+
@basic_auth.instance_eval { @auth_scheme = webrick_backup }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
153
166
|
# To work this test consistently on CRuby you can to add 'Thread.pass' in
|
|
154
167
|
# @challenge iteration at BasicAuth#get like;
|
|
155
168
|
#
|
data/test/test_http-access2.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
1
2
|
require 'http-access2'
|
|
2
3
|
require File.expand_path('helper', File.dirname(__FILE__))
|
|
3
4
|
|
|
@@ -116,7 +117,7 @@ class TestClient < Test::Unit::TestCase
|
|
|
116
117
|
setup_proxyserver
|
|
117
118
|
escape_noproxy do
|
|
118
119
|
begin
|
|
119
|
-
@client.proxy = "http
|
|
120
|
+
@client.proxy = "http://あ"
|
|
120
121
|
rescue
|
|
121
122
|
assert_match(/InvalidURIError/, $!.class.to_s)
|
|
122
123
|
end
|
data/test/test_httpclient.rb
CHANGED
data/test/test_ssl.rb
CHANGED
|
@@ -33,7 +33,10 @@ class TestSSL < Test::Unit::TestCase
|
|
|
33
33
|
assert_equal(OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT, cfg.verify_mode)
|
|
34
34
|
assert_nil(cfg.verify_callback)
|
|
35
35
|
assert_nil(cfg.timeout)
|
|
36
|
-
|
|
36
|
+
expected_options = OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2 | OpenSSL::SSL::OP_NO_SSLv3
|
|
37
|
+
expected_options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS)
|
|
38
|
+
expected_options |= OpenSSL::SSL::OP_NO_COMPRESSION if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
|
|
39
|
+
assert_equal(expected_options, cfg.options)
|
|
37
40
|
assert_equal("ALL:!aNULL:!eNULL:!SSLv2", cfg.ciphers)
|
|
38
41
|
assert_instance_of(OpenSSL::X509::Store, cfg.cert_store)
|
|
39
42
|
end
|
|
@@ -166,6 +169,24 @@ end
|
|
|
166
169
|
end
|
|
167
170
|
end
|
|
168
171
|
|
|
172
|
+
def test_no_sslv3
|
|
173
|
+
teardown_server
|
|
174
|
+
setup_server_with_ssl_version(:SSLv3)
|
|
175
|
+
assert_raise(OpenSSL::SSL::SSLError) do
|
|
176
|
+
@client.ssl_config.verify_mode = nil
|
|
177
|
+
@client.get("https://localhost:#{serverport}/hello")
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def test_allow_tlsv1
|
|
182
|
+
teardown_server
|
|
183
|
+
setup_server#_with_ssl_version(:TLSv1)
|
|
184
|
+
assert_nothing_raised do
|
|
185
|
+
@client.ssl_config.verify_mode = nil
|
|
186
|
+
@client.get("https://localhost:#{serverport}/hello")
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
169
190
|
private
|
|
170
191
|
|
|
171
192
|
def cert(filename)
|
|
@@ -207,6 +228,31 @@ private
|
|
|
207
228
|
@server_thread = start_server_thread(@server)
|
|
208
229
|
end
|
|
209
230
|
|
|
231
|
+
def setup_server_with_ssl_version(ssl_version)
|
|
232
|
+
logger = Logger.new(STDERR)
|
|
233
|
+
#logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level)
|
|
234
|
+
@server = WEBrick::HTTPServer.new(
|
|
235
|
+
:BindAddress => "localhost",
|
|
236
|
+
:Logger => logger,
|
|
237
|
+
:Port => 0,
|
|
238
|
+
:AccessLog => [],
|
|
239
|
+
:DocumentRoot => DIR,
|
|
240
|
+
:SSLEnable => true,
|
|
241
|
+
:SSLCACertificateFile => File.join(DIR, 'ca.cert'),
|
|
242
|
+
:SSLCertificate => cert('server.cert'),
|
|
243
|
+
:SSLPrivateKey => key('server.key')
|
|
244
|
+
)
|
|
245
|
+
@server.ssl_context.ssl_version = ssl_version
|
|
246
|
+
@serverport = @server.config[:Port]
|
|
247
|
+
[:hello].each do |sym|
|
|
248
|
+
@server.mount(
|
|
249
|
+
"/#{sym}",
|
|
250
|
+
WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
|
|
251
|
+
)
|
|
252
|
+
end
|
|
253
|
+
@server_thread = start_server_thread(@server)
|
|
254
|
+
end
|
|
255
|
+
|
|
210
256
|
def do_hello(req, res)
|
|
211
257
|
res['content-type'] = 'text/html'
|
|
212
258
|
res.body = "hello"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: httpclient
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hiroshi Nakamura
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-
|
|
11
|
+
date: 2014-10-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description:
|
|
14
14
|
email: nahi@ruby-lang.org
|
|
@@ -41,6 +41,7 @@ files:
|
|
|
41
41
|
- sample/auth.rb
|
|
42
42
|
- sample/cookie.rb
|
|
43
43
|
- sample/dav.rb
|
|
44
|
+
- sample/github.rb
|
|
44
45
|
- sample/howto.rb
|
|
45
46
|
- sample/oauth_buzz.rb
|
|
46
47
|
- sample/oauth_friendfeed.rb
|
|
@@ -84,7 +85,7 @@ files:
|
|
|
84
85
|
- test/test_httpclient.rb
|
|
85
86
|
- test/test_include_client.rb
|
|
86
87
|
- test/test_ssl.rb
|
|
87
|
-
homepage:
|
|
88
|
+
homepage: https://github.com/nahi/httpclient
|
|
88
89
|
licenses:
|
|
89
90
|
- ruby
|
|
90
91
|
metadata: {}
|
|
@@ -104,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
104
105
|
version: '0'
|
|
105
106
|
requirements: []
|
|
106
107
|
rubyforge_project:
|
|
107
|
-
rubygems_version: 2.
|
|
108
|
+
rubygems_version: 2.4.1
|
|
108
109
|
signing_key:
|
|
109
110
|
specification_version: 4
|
|
110
111
|
summary: gives something like the functionality of libwww-perl (LWP) in Ruby
|