httpclient 2.1.7.2 → 2.2.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.
- data/lib/httpclient.rb +141 -91
- data/lib/httpclient/auth.rb +2 -2
- data/lib/httpclient/http.rb +69 -38
- data/lib/httpclient/session.rb +2 -2
- data/lib/httpclient/ssl_config.rb +20 -6
- data/lib/httpclient/timeout.rb +2 -0
- data/lib/httpclient/util.rb +27 -3
- metadata +3 -5
data/lib/httpclient.rb
CHANGED
@@ -50,13 +50,23 @@ require 'httpclient/cookie'
|
|
50
50
|
#
|
51
51
|
# === How to retrieve web resources
|
52
52
|
#
|
53
|
-
# See get_content.
|
53
|
+
# See get and get_content.
|
54
54
|
#
|
55
|
-
# 1. Get content of specified URL. It returns
|
55
|
+
# 1. Get content of specified URL. It returns HTTP::Message object and
|
56
|
+
# calling 'body' method of it returns a content String.
|
57
|
+
#
|
58
|
+
# puts clnt.get('http://dev.ctor.org/').body
|
59
|
+
#
|
60
|
+
# 2. For getting content directly, use get_content. It follows redirect
|
61
|
+
# response and returns a String of whole result.
|
56
62
|
#
|
57
63
|
# puts clnt.get_content('http://dev.ctor.org/')
|
58
64
|
#
|
59
|
-
#
|
65
|
+
# 3. You can pass :follow_redirect option to follow redirect response in get.
|
66
|
+
#
|
67
|
+
# puts clnt.get('http://dev.ctor.org/', :foolow_redirect => true)
|
68
|
+
#
|
69
|
+
# 4. Get content as chunks of String. It yields chunks of String.
|
60
70
|
#
|
61
71
|
# clnt.get_content('http://dev.ctor.org/') do |chunk|
|
62
72
|
# puts chunk
|
@@ -79,7 +89,11 @@ require 'httpclient/cookie'
|
|
79
89
|
# p res.status
|
80
90
|
# p res.contenttype
|
81
91
|
# p res.header['X-Custom']
|
82
|
-
# puts res.
|
92
|
+
# puts res.body
|
93
|
+
#
|
94
|
+
# You can also use keyword argument style.
|
95
|
+
#
|
96
|
+
# res = clnt.get(uri, :query => { :keyword => 'ruby', :lang => 'en' })
|
83
97
|
#
|
84
98
|
# === How to POST
|
85
99
|
#
|
@@ -90,6 +104,10 @@ require 'httpclient/cookie'
|
|
90
104
|
# body = { 'keyword' => 'ruby', 'lang' => 'en' }
|
91
105
|
# res = clnt.post(uri, body)
|
92
106
|
#
|
107
|
+
# Keyword argument style.
|
108
|
+
#
|
109
|
+
# res = clnt.post(uri, :body => ...)
|
110
|
+
#
|
93
111
|
# 2. Do multipart file upload with POST. No need to set extra header by
|
94
112
|
# yourself from httpclient/2.1.4.
|
95
113
|
#
|
@@ -117,7 +135,7 @@ require 'httpclient/cookie'
|
|
117
135
|
# Just pass an URL which starts with 'https://'.
|
118
136
|
#
|
119
137
|
# https_url = 'https://www.rsa.com'
|
120
|
-
# clnt.
|
138
|
+
# clnt.get(https_url)
|
121
139
|
#
|
122
140
|
# 2. Getting peer certificate from response.
|
123
141
|
#
|
@@ -129,22 +147,23 @@ require 'httpclient/cookie'
|
|
129
147
|
# user_cert_file = 'cert.pem'
|
130
148
|
# user_key_file = 'privkey.pem'
|
131
149
|
# clnt.ssl_config.set_client_cert_file(user_cert_file, user_key_file)
|
132
|
-
# clnt.
|
150
|
+
# clnt.get(https_url)
|
133
151
|
#
|
134
152
|
# === Handling Cookies
|
135
153
|
#
|
136
154
|
# 1. Using volatile Cookies. Nothing to do. HTTPClient handles Cookies.
|
137
155
|
#
|
138
156
|
# clnt = HTTPClient.new
|
139
|
-
# clnt.
|
140
|
-
# clnt.
|
157
|
+
# res = clnt.get(url1) # receives Cookies.
|
158
|
+
# res = clnt.get(url2) # sends Cookies if needed.
|
159
|
+
# p res.cookies
|
141
160
|
#
|
142
161
|
# 2. Saving non volatile Cookies to a specified file. Need to set a file at
|
143
162
|
# first and invoke save method at last.
|
144
163
|
#
|
145
164
|
# clnt = HTTPClient.new
|
146
165
|
# clnt.set_cookie_store('/home/nahi/cookie.dat')
|
147
|
-
# clnt.
|
166
|
+
# clnt.get(url)
|
148
167
|
# ...
|
149
168
|
# clnt.save_cookie_store
|
150
169
|
#
|
@@ -163,7 +182,7 @@ require 'httpclient/cookie'
|
|
163
182
|
# user = 'user'
|
164
183
|
# password = 'user'
|
165
184
|
# clnt.set_auth(domain, user, password)
|
166
|
-
# p clnt.
|
185
|
+
# p clnt.get('http://dev.ctor.org/http-access2/login').status
|
167
186
|
#
|
168
187
|
# 2. Authentication with Proxy server. Supports BasicAuth and NTLM
|
169
188
|
# (requires win32/sspi)
|
@@ -172,17 +191,17 @@ require 'httpclient/cookie'
|
|
172
191
|
# user = 'proxy'
|
173
192
|
# password = 'proxy'
|
174
193
|
# clnt.set_proxy_auth(user, password)
|
175
|
-
# p clnt.
|
194
|
+
# p clnt.get(url)
|
176
195
|
#
|
177
196
|
# === Invoking HTTP methods with custom header
|
178
197
|
#
|
179
|
-
# Pass a Hash or an Array for
|
198
|
+
# Pass a Hash or an Array for header argument.
|
180
199
|
#
|
181
|
-
#
|
182
|
-
# clnt.
|
200
|
+
# header = { 'Accept' => '*/*' }
|
201
|
+
# clnt.get(uri, query, header)
|
183
202
|
#
|
184
|
-
#
|
185
|
-
# clnt.get_content(uri, query,
|
203
|
+
# header = [['Accept', 'image/jpeg'], ['Accept', 'image/png']]
|
204
|
+
# clnt.get_content(uri, query, header)
|
186
205
|
#
|
187
206
|
# === Invoking HTTP methods asynchronously
|
188
207
|
#
|
@@ -200,7 +219,7 @@ require 'httpclient/cookie'
|
|
200
219
|
# puts '.'
|
201
220
|
# res = connection.pop
|
202
221
|
# p res.status
|
203
|
-
# p res.
|
222
|
+
# p res.body.read # res.body is an IO for the res of async method.
|
204
223
|
#
|
205
224
|
# === Shortcut methods
|
206
225
|
#
|
@@ -210,7 +229,7 @@ require 'httpclient/cookie'
|
|
210
229
|
# ruby -rhttpclient -e 'p HTTPClient.head(ARGV.shift).header["last-modified"]' http://dev.ctor.org/
|
211
230
|
#
|
212
231
|
class HTTPClient
|
213
|
-
VERSION = '2.
|
232
|
+
VERSION = '2.2.0'
|
214
233
|
RUBY_VERSION_STRING = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
215
234
|
/: (\S+) (\S+)/ =~ %q$Id$
|
216
235
|
LIB_NAME = "(#{$1}/#{$2}, #{RUBY_VERSION_STRING})"
|
@@ -333,7 +352,7 @@ class HTTPClient
|
|
333
352
|
# Local socket address. Set HTTPClient#socket_local.host and HTTPClient#socket_local.port to specify local binding hostname and port of TCP socket.
|
334
353
|
attr_proxy(:socket_local, true)
|
335
354
|
|
336
|
-
# Default
|
355
|
+
# Default header for PROPFIND request.
|
337
356
|
PROPFIND_DEFAULT_EXTHEADER = { 'Depth' => '0' }
|
338
357
|
|
339
358
|
# Creates a HTTPClient instance which manages sessions, cookies, etc.
|
@@ -497,6 +516,13 @@ class HTTPClient
|
|
497
516
|
@cookie_manager.save_cookies
|
498
517
|
end
|
499
518
|
|
519
|
+
# Returns stored cookies.
|
520
|
+
def cookies
|
521
|
+
if @cookie_manager
|
522
|
+
@cookie_manager.cookies
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
500
526
|
# Sets callback proc when HTTP redirect status is returned for get_content
|
501
527
|
# and post_content. default_redirect_uri_callback is used by default.
|
502
528
|
#
|
@@ -516,9 +542,9 @@ class HTTPClient
|
|
516
542
|
# e.g. { "a" => "b" } => 'http://host/part?a=b'.
|
517
543
|
# Give an array to pass multiple value like
|
518
544
|
# [["a", "b"], ["a", "c"]] => 'http://host/part?a=b&a=c'.
|
519
|
-
#
|
520
|
-
#
|
521
|
-
#
|
545
|
+
# header:: a Hash or an Array of extra headers. e.g.
|
546
|
+
# { 'Accept' => '*/*' } or
|
547
|
+
# [['Accept', 'image/jpeg'], ['Accept', 'image/png']].
|
522
548
|
# &block:: Give a block to get chunked message-body of response like
|
523
549
|
# get_content(uri) { |chunked_body| ... }.
|
524
550
|
# Size of each chunk may not be the same.
|
@@ -530,8 +556,9 @@ class HTTPClient
|
|
530
556
|
# If you need to get full HTTP response including HTTP status and headers,
|
531
557
|
# use get method. get returns HTTP::Message as a response and you need to
|
532
558
|
# follow HTTP redirect by yourself if you need.
|
533
|
-
def get_content(uri,
|
534
|
-
|
559
|
+
def get_content(uri, *args, &block)
|
560
|
+
query, header = keyword_argument(args, :query, :header)
|
561
|
+
follow_redirect(:get, uri, query, nil, header || {}, &block).content
|
535
562
|
end
|
536
563
|
|
537
564
|
# Posts a content.
|
@@ -550,10 +577,10 @@ class HTTPClient
|
|
550
577
|
# [{ 'Content-Type' => 'text/plain', :content => "some text" },
|
551
578
|
# { 'Content-Type' => 'video/mp4', :content => File.new('video.mp4') }]
|
552
579
|
# => <Two parts with custom Content-Type header>
|
553
|
-
#
|
554
|
-
#
|
555
|
-
#
|
556
|
-
#
|
580
|
+
# header:: a Hash or an Array of extra headers. e.g.
|
581
|
+
# { 'Accept' => '*/*' }
|
582
|
+
# or
|
583
|
+
# [['Accept', 'image/jpeg'], ['Accept', 'image/png']].
|
557
584
|
# &block:: Give a block to get chunked message-body of response like
|
558
585
|
# post_content(uri) { |chunked_body| ... }.
|
559
586
|
# Size of each chunk may not be the same.
|
@@ -564,8 +591,9 @@ class HTTPClient
|
|
564
591
|
#
|
565
592
|
# If you need to get full HTTP response including HTTP status and headers,
|
566
593
|
# use post method.
|
567
|
-
def post_content(uri,
|
568
|
-
|
594
|
+
def post_content(uri, *args, &block)
|
595
|
+
body, header = keyword_argument(args, :body, :header)
|
596
|
+
follow_redirect(:post, uri, nil, body, header || {}, &block).content
|
569
597
|
end
|
570
598
|
|
571
599
|
# A method for redirect uri callback. How to use:
|
@@ -605,48 +633,48 @@ class HTTPClient
|
|
605
633
|
end
|
606
634
|
|
607
635
|
# Sends HEAD request to the specified URL. See request for arguments.
|
608
|
-
def head(uri,
|
609
|
-
request(:head, uri,
|
636
|
+
def head(uri, *args)
|
637
|
+
request(:head, uri, argument_to_hash(args, :query, :header))
|
610
638
|
end
|
611
639
|
|
612
640
|
# Sends GET request to the specified URL. See request for arguments.
|
613
|
-
def get(uri,
|
614
|
-
request(:get, uri, query,
|
641
|
+
def get(uri, *args, &block)
|
642
|
+
request(:get, uri, argument_to_hash(args, :query, :header, :follow_redirect), &block)
|
615
643
|
end
|
616
644
|
|
617
645
|
# Sends POST request to the specified URL. See request for arguments.
|
618
|
-
def post(uri,
|
619
|
-
request(:post, uri,
|
646
|
+
def post(uri, *args, &block)
|
647
|
+
request(:post, uri, argument_to_hash(args, :body, :header, :follow_redirect), &block)
|
620
648
|
end
|
621
649
|
|
622
650
|
# Sends PUT request to the specified URL. See request for arguments.
|
623
|
-
def put(uri,
|
624
|
-
request(:put, uri,
|
651
|
+
def put(uri, *args, &block)
|
652
|
+
request(:put, uri, argument_to_hash(args, :body, :header), &block)
|
625
653
|
end
|
626
654
|
|
627
655
|
# Sends DELETE request to the specified URL. See request for arguments.
|
628
|
-
def delete(uri,
|
629
|
-
request(:delete, uri,
|
656
|
+
def delete(uri, *args, &block)
|
657
|
+
request(:delete, uri, argument_to_hash(args, :header), &block)
|
630
658
|
end
|
631
659
|
|
632
660
|
# Sends OPTIONS request to the specified URL. See request for arguments.
|
633
|
-
def options(uri,
|
634
|
-
request(:options, uri,
|
661
|
+
def options(uri, *args, &block)
|
662
|
+
request(:options, uri, argument_to_hash(args, :header), &block)
|
635
663
|
end
|
636
664
|
|
637
665
|
# Sends PROPFIND request to the specified URL. See request for arguments.
|
638
|
-
def propfind(uri,
|
639
|
-
request(:propfind, uri,
|
666
|
+
def propfind(uri, *args, &block)
|
667
|
+
request(:propfind, uri, argument_to_hash(args, :header), &block)
|
640
668
|
end
|
641
669
|
|
642
670
|
# Sends PROPPATCH request to the specified URL. See request for arguments.
|
643
|
-
def proppatch(uri,
|
644
|
-
request(:proppatch, uri,
|
671
|
+
def proppatch(uri, *args, &block)
|
672
|
+
request(:proppatch, uri, argument_to_hash(args, :body, :header), &block)
|
645
673
|
end
|
646
674
|
|
647
675
|
# Sends TRACE request to the specified URL. See request for arguments.
|
648
|
-
def trace(uri,
|
649
|
-
request('TRACE', uri, query, body,
|
676
|
+
def trace(uri, *args, &block)
|
677
|
+
request('TRACE', uri, argument_to_hash(args, :query, :body, :header), &block)
|
650
678
|
end
|
651
679
|
|
652
680
|
# Sends a request to the specified URL.
|
@@ -673,9 +701,9 @@ class HTTPClient
|
|
673
701
|
# { 'Content-Type' => 'video/mp4', :content => File.new('video.mp4') }]
|
674
702
|
# => <Two parts with custom Content-Type header>
|
675
703
|
# See HTTP::Message.file? for actual condition of 'a file'.
|
676
|
-
#
|
677
|
-
#
|
678
|
-
#
|
704
|
+
# header:: a Hash or an Array of extra headers. e.g.
|
705
|
+
# { 'Accept' => '*/*' } or
|
706
|
+
# [['Accept', 'image/jpeg'], ['Accept', 'image/png']].
|
679
707
|
# &block:: Give a block to get chunked message-body of response like
|
680
708
|
# get(uri) { |chunked_body| ... }.
|
681
709
|
# Size of each chunk may not be the same.
|
@@ -687,77 +715,99 @@ class HTTPClient
|
|
687
715
|
# chunked encoding (Transfer-Encoding: chunked in HTTP header). Bear in mind
|
688
716
|
# that some server application does not support chunked request. At least
|
689
717
|
# cgi.rb does not support it.
|
690
|
-
def request(method, uri,
|
718
|
+
def request(method, uri, *args, &block)
|
719
|
+
query, body, header, follow_redirect = keyword_argument(args, :query, :body, :header, :follow_redirect)
|
720
|
+
if [:post, :put].include?(method)
|
721
|
+
body ||= ''
|
722
|
+
end
|
723
|
+
if method == :propfind
|
724
|
+
header ||= PROPFIND_DEFAULT_EXTHEADER
|
725
|
+
else
|
726
|
+
header ||= {}
|
727
|
+
end
|
691
728
|
uri = urify(uri)
|
692
729
|
if block
|
693
730
|
filtered_block = proc { |res, str|
|
694
731
|
block.call(str)
|
695
732
|
}
|
696
733
|
end
|
697
|
-
|
734
|
+
if follow_redirect
|
735
|
+
follow_redirect(method, uri, query, body, header, &block)
|
736
|
+
else
|
737
|
+
do_request(method, uri, query, body, header, &filtered_block)
|
738
|
+
end
|
698
739
|
end
|
699
740
|
|
700
741
|
# Sends HEAD request in async style. See request_async for arguments.
|
701
742
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
702
|
-
def head_async(uri,
|
703
|
-
|
743
|
+
def head_async(uri, *args)
|
744
|
+
query, header = keyword_argument(args, :query, :header)
|
745
|
+
request_async(:head, uri, query, nil, header || {})
|
704
746
|
end
|
705
747
|
|
706
748
|
# Sends GET request in async style. See request_async for arguments.
|
707
749
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
708
|
-
def get_async(uri,
|
709
|
-
|
750
|
+
def get_async(uri, *args)
|
751
|
+
query, header = keyword_argument(args, :query, :header)
|
752
|
+
request_async(:get, uri, query, nil, header || {})
|
710
753
|
end
|
711
754
|
|
712
755
|
# Sends POST request in async style. See request_async for arguments.
|
713
756
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
714
|
-
def post_async(uri,
|
715
|
-
|
757
|
+
def post_async(uri, *args)
|
758
|
+
body, header = keyword_argument(args, :body, :header)
|
759
|
+
request_async(:post, uri, nil, body || '', header || {})
|
716
760
|
end
|
717
761
|
|
718
762
|
# Sends PUT request in async style. See request_async for arguments.
|
719
763
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
720
|
-
def put_async(uri,
|
721
|
-
|
764
|
+
def put_async(uri, *args)
|
765
|
+
body, header = keyword_argument(args, :body, :header)
|
766
|
+
request_async(:put, uri, nil, body || '', header || {})
|
722
767
|
end
|
723
768
|
|
724
769
|
# Sends DELETE request in async style. See request_async for arguments.
|
725
770
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
726
|
-
def delete_async(uri,
|
727
|
-
|
771
|
+
def delete_async(uri, *args)
|
772
|
+
header = keyword_argument(args, :header)
|
773
|
+
request_async(:delete, uri, nil, nil, header || {})
|
728
774
|
end
|
729
775
|
|
730
776
|
# Sends OPTIONS request in async style. See request_async for arguments.
|
731
777
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
732
|
-
def options_async(uri,
|
733
|
-
|
778
|
+
def options_async(uri, *args)
|
779
|
+
header = keyword_argument(args, :header)
|
780
|
+
request_async(:options, uri, nil, nil, header || {})
|
734
781
|
end
|
735
782
|
|
736
783
|
# Sends PROPFIND request in async style. See request_async for arguments.
|
737
784
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
738
|
-
def propfind_async(uri,
|
739
|
-
|
785
|
+
def propfind_async(uri, *args)
|
786
|
+
header = keyword_argument(args, :header)
|
787
|
+
request_async(:propfind, uri, nil, nil, header || PROPFIND_DEFAULT_EXTHEADER)
|
740
788
|
end
|
741
789
|
|
742
790
|
# Sends PROPPATCH request in async style. See request_async for arguments.
|
743
791
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
744
|
-
def proppatch_async(uri,
|
745
|
-
|
792
|
+
def proppatch_async(uri, *args)
|
793
|
+
body, header = keyword_argument(args, :body, :header)
|
794
|
+
request_async(:proppatch, uri, nil, body, header || {})
|
746
795
|
end
|
747
796
|
|
748
797
|
# Sends TRACE request in async style. See request_async for arguments.
|
749
798
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
750
|
-
def trace_async(uri,
|
751
|
-
|
799
|
+
def trace_async(uri, *args)
|
800
|
+
query, body, header = keyword_argument(args, :query, :body, :header)
|
801
|
+
request_async(:trace, uri, query, body, header || {})
|
752
802
|
end
|
753
803
|
|
754
804
|
# Sends a request in async style. request method creates new Thread for
|
755
805
|
# HTTP connection and returns a HTTPClient::Connection instance immediately.
|
756
806
|
#
|
757
807
|
# Arguments definition is the same as request.
|
758
|
-
def request_async(method, uri, query = nil, body = nil,
|
808
|
+
def request_async(method, uri, query = nil, body = nil, header = {})
|
759
809
|
uri = urify(uri)
|
760
|
-
do_request_async(method, uri, query, body,
|
810
|
+
do_request_async(method, uri, query, body, header)
|
761
811
|
end
|
762
812
|
|
763
813
|
# Resets internal session for the given URL. Keep-alive connection for the
|
@@ -784,7 +834,7 @@ private
|
|
784
834
|
end
|
785
835
|
end
|
786
836
|
|
787
|
-
def do_request(method, uri, query, body,
|
837
|
+
def do_request(method, uri, query, body, header, &block)
|
788
838
|
conn = Connection.new
|
789
839
|
res = nil
|
790
840
|
if HTTP::Message.file?(body)
|
@@ -794,7 +844,7 @@ private
|
|
794
844
|
proxy = no_proxy?(uri) ? nil : @proxy
|
795
845
|
while retry_count > 0
|
796
846
|
body.pos = pos if pos
|
797
|
-
req = create_request(method, uri, query, body,
|
847
|
+
req = create_request(method, uri, query, body, header)
|
798
848
|
begin
|
799
849
|
protect_keep_alive_disconnected do
|
800
850
|
do_get_block(req, proxy, conn, &block)
|
@@ -809,7 +859,7 @@ private
|
|
809
859
|
res
|
810
860
|
end
|
811
861
|
|
812
|
-
def do_request_async(method, uri, query, body,
|
862
|
+
def do_request_async(method, uri, query, body, header)
|
813
863
|
conn = Connection.new
|
814
864
|
t = Thread.new(conn) { |tconn|
|
815
865
|
begin
|
@@ -820,7 +870,7 @@ private
|
|
820
870
|
proxy = no_proxy?(uri) ? nil : @proxy
|
821
871
|
while retry_count > 0
|
822
872
|
body.pos = pos if pos
|
823
|
-
req = create_request(method, uri, query, body,
|
873
|
+
req = create_request(method, uri, query, body, header)
|
824
874
|
begin
|
825
875
|
protect_keep_alive_disconnected do
|
826
876
|
do_get_stream(req, proxy, tconn)
|
@@ -856,7 +906,7 @@ private
|
|
856
906
|
ENV[name.downcase] || ENV[name.upcase]
|
857
907
|
end
|
858
908
|
|
859
|
-
def follow_redirect(method, uri, query, body,
|
909
|
+
def follow_redirect(method, uri, query, body, header, &block)
|
860
910
|
uri = urify(uri)
|
861
911
|
if block
|
862
912
|
filtered_block = proc { |r, str|
|
@@ -869,7 +919,7 @@ private
|
|
869
919
|
retry_number = 0
|
870
920
|
while retry_number < @follow_redirect_count
|
871
921
|
body.pos = pos if pos
|
872
|
-
res = do_request(method, uri, query, body,
|
922
|
+
res = do_request(method, uri, query, body, header, &filtered_block)
|
873
923
|
if HTTP::Status.successful?(res.status)
|
874
924
|
return res
|
875
925
|
elsif HTTP::Status.redirect?(res.status)
|
@@ -893,16 +943,16 @@ private
|
|
893
943
|
end
|
894
944
|
end
|
895
945
|
|
896
|
-
def create_request(method, uri, query, body,
|
946
|
+
def create_request(method, uri, query, body, header)
|
897
947
|
method = method.to_s.upcase
|
898
|
-
if
|
899
|
-
|
948
|
+
if header.is_a?(Hash)
|
949
|
+
header = header.to_a
|
900
950
|
else
|
901
|
-
|
951
|
+
header = header.dup
|
902
952
|
end
|
903
953
|
boundary = nil
|
904
954
|
if body
|
905
|
-
_, content_type =
|
955
|
+
_, content_type = header.find { |key, value|
|
906
956
|
key.downcase == 'content-type'
|
907
957
|
}
|
908
958
|
if content_type
|
@@ -912,7 +962,7 @@ private
|
|
912
962
|
else
|
913
963
|
boundary = create_boundary
|
914
964
|
content_type = "#{content_type}; boundary=#{boundary}"
|
915
|
-
|
965
|
+
header = override_header(header, 'Content-Type', content_type)
|
916
966
|
end
|
917
967
|
end
|
918
968
|
elsif method == 'POST'
|
@@ -922,11 +972,11 @@ private
|
|
922
972
|
else
|
923
973
|
content_type = 'application/x-www-form-urlencoded'
|
924
974
|
end
|
925
|
-
|
975
|
+
header << ['Content-Type', content_type]
|
926
976
|
end
|
927
977
|
end
|
928
978
|
req = HTTP::Message.new_request(method, uri, query, body, boundary)
|
929
|
-
|
979
|
+
header.each do |key, value|
|
930
980
|
req.header.add(key, value)
|
931
981
|
end
|
932
982
|
if @cookie_manager && cookie = @cookie_manager.find(uri)
|
@@ -944,9 +994,9 @@ private
|
|
944
994
|
body.any? { |k, v| HTTP::Message.file?(v) }
|
945
995
|
end
|
946
996
|
|
947
|
-
def override_header(
|
997
|
+
def override_header(header, key, value)
|
948
998
|
result = []
|
949
|
-
|
999
|
+
header.each do |k, v|
|
950
1000
|
if k.downcase == key.downcase
|
951
1001
|
result << [key, value]
|
952
1002
|
else
|
@@ -981,7 +1031,7 @@ private
|
|
981
1031
|
filter.filter_request(req)
|
982
1032
|
end
|
983
1033
|
if str = @test_loopback_response.shift
|
984
|
-
dump_dummy_request_response(req.
|
1034
|
+
dump_dummy_request_response(req.http_body.dump, str) if @debug_dev
|
985
1035
|
conn.push(HTTP::Message.new_response(str))
|
986
1036
|
return
|
987
1037
|
end
|
@@ -1017,7 +1067,7 @@ private
|
|
1017
1067
|
filter.filter_request(req)
|
1018
1068
|
end
|
1019
1069
|
if str = @test_loopback_response.shift
|
1020
|
-
dump_dummy_request_response(req.
|
1070
|
+
dump_dummy_request_response(req.http_body.dump, str) if @debug_dev
|
1021
1071
|
conn.push(HTTP::Message.new_response(StringIO.new(str)))
|
1022
1072
|
return
|
1023
1073
|
end
|
data/lib/httpclient/auth.rb
CHANGED
@@ -800,8 +800,8 @@ class HTTPClient
|
|
800
800
|
params += encode_param(query)
|
801
801
|
end
|
802
802
|
# captures HTTP Message body only for 'application/x-www-form-urlencoded'
|
803
|
-
if req.header.contenttype == 'application/x-www-form-urlencoded' and req.
|
804
|
-
params += encode_param(HTTP::Message.parse(req.
|
803
|
+
if req.header.contenttype == 'application/x-www-form-urlencoded' and req.http_body.size
|
804
|
+
params += encode_param(HTTP::Message.parse(req.http_body.content))
|
805
805
|
end
|
806
806
|
uri = req.header.request_uri
|
807
807
|
if uri.query
|
data/lib/httpclient/http.rb
CHANGED
@@ -378,12 +378,17 @@ module HTTP
|
|
378
378
|
set('Last-Modified', @body_date.httpdate)
|
379
379
|
end
|
380
380
|
if self['Content-Type'].empty?
|
381
|
-
set('Content-Type', "#{ @body_type || 'text/html' }; charset=#{ charset_label
|
381
|
+
set('Content-Type', "#{ @body_type || 'text/html' }; charset=#{ charset_label }")
|
382
382
|
end
|
383
383
|
end
|
384
384
|
|
385
|
-
def charset_label
|
386
|
-
|
385
|
+
def charset_label
|
386
|
+
# TODO: should handle response encoding for 1.9 correctly.
|
387
|
+
if RUBY_VERSION > "1.9"
|
388
|
+
CHARSET_MAP[@body_charset] || 'us-ascii'
|
389
|
+
else
|
390
|
+
CHARSET_MAP[@body_charset || $KCODE] || 'us-ascii'
|
391
|
+
end
|
387
392
|
end
|
388
393
|
end
|
389
394
|
|
@@ -640,8 +645,8 @@ module HTTP
|
|
640
645
|
# uri:: an URI that need to connect. Only uri.host and uri.port are used.
|
641
646
|
def new_connect_request(uri)
|
642
647
|
m = new
|
643
|
-
m.
|
644
|
-
m.
|
648
|
+
m.http_header.init_connect_request(uri)
|
649
|
+
m.http_header.body_size = nil
|
645
650
|
m
|
646
651
|
end
|
647
652
|
|
@@ -660,14 +665,14 @@ module HTTP
|
|
660
665
|
# a multipart/form-data using this boundary String.
|
661
666
|
def new_request(method, uri, query = nil, body = nil, boundary = nil)
|
662
667
|
m = new
|
663
|
-
m.
|
664
|
-
m.
|
665
|
-
m.
|
668
|
+
m.http_header.init_request(method, uri, query)
|
669
|
+
m.http_body = Body.new
|
670
|
+
m.http_body.init_request(body || '', boundary)
|
666
671
|
if body
|
667
|
-
m.
|
668
|
-
m.
|
672
|
+
m.http_header.body_size = m.http_body.size
|
673
|
+
m.http_header.chunked = true if m.http_body.size.nil?
|
669
674
|
else
|
670
|
-
m.
|
675
|
+
m.http_header.body_size = nil
|
671
676
|
end
|
672
677
|
m
|
673
678
|
end
|
@@ -676,10 +681,10 @@ module HTTP
|
|
676
681
|
# body:: a String or an IO of response message body.
|
677
682
|
def new_response(body)
|
678
683
|
m = new
|
679
|
-
m.
|
680
|
-
m.
|
681
|
-
m.
|
682
|
-
m.
|
684
|
+
m.http_header.init_response(Status::OK)
|
685
|
+
m.http_body = Body.new
|
686
|
+
m.http_body.init_response(body)
|
687
|
+
m.http_header.body_size = m.http_body.size || 0
|
683
688
|
m
|
684
689
|
end
|
685
690
|
|
@@ -820,10 +825,10 @@ module HTTP
|
|
820
825
|
|
821
826
|
|
822
827
|
# HTTP::Message::Headers:: message header.
|
823
|
-
attr_accessor :
|
828
|
+
attr_accessor :http_header
|
824
829
|
|
825
830
|
# HTTP::Message::Body:: message body.
|
826
|
-
attr_reader :
|
831
|
+
attr_reader :http_body
|
827
832
|
|
828
833
|
# OpenSSL::X509::Certificate:: response only. server certificate which is
|
829
834
|
# used for retrieving the response.
|
@@ -833,18 +838,18 @@ module HTTP
|
|
833
838
|
# Use Message.new_connect_request, Message.new_request or
|
834
839
|
# Message.new_response instead.
|
835
840
|
def initialize # :nodoc:
|
836
|
-
@
|
837
|
-
@
|
841
|
+
@http_header = Headers.new
|
842
|
+
@http_body = @peer_cert = nil
|
838
843
|
end
|
839
844
|
|
840
845
|
# Dumps message (header and body) to given dev.
|
841
846
|
# dev needs to respond to <<.
|
842
847
|
def dump(dev = '')
|
843
|
-
str =
|
844
|
-
if
|
845
|
-
dev =
|
846
|
-
elsif
|
847
|
-
dev =
|
848
|
+
str = @http_header.dump + CRLF
|
849
|
+
if @http_header.chunked
|
850
|
+
dev = @http_body.dump_chunked(str, dev)
|
851
|
+
elsif @http_body
|
852
|
+
dev = @http_body.dump(str, dev)
|
848
853
|
else
|
849
854
|
dev << str
|
850
855
|
end
|
@@ -852,35 +857,35 @@ module HTTP
|
|
852
857
|
end
|
853
858
|
|
854
859
|
# Sets a new body. header.body_size is updated with new body.size.
|
855
|
-
def
|
856
|
-
@
|
857
|
-
@
|
860
|
+
def http_body=(body)
|
861
|
+
@http_body = body
|
862
|
+
@http_header.body_size = @http_body.size if @http_header
|
858
863
|
end
|
859
864
|
|
860
865
|
# Returns HTTP version in a HTTP header. String.
|
861
866
|
def http_version
|
862
|
-
@
|
867
|
+
@http_header.http_version
|
863
868
|
end
|
864
869
|
|
865
870
|
# Sets HTTP version in a HTTP header. String.
|
866
871
|
def http_version=(http_version)
|
867
|
-
@
|
872
|
+
@http_header.http_version = http_version
|
868
873
|
end
|
869
874
|
|
870
875
|
VERSION_WARNING = 'Message#version (Float) is deprecated. Use Message#http_version (String) instead.'
|
871
876
|
def version
|
872
877
|
warn(VERSION_WARNING)
|
873
|
-
@
|
878
|
+
@http_header.http_version.to_f
|
874
879
|
end
|
875
880
|
|
876
881
|
def version=(version)
|
877
882
|
warn(VERSION_WARNING)
|
878
|
-
@
|
883
|
+
@http_header.http_version = version
|
879
884
|
end
|
880
885
|
|
881
886
|
# Returns HTTP status code in response. Integer.
|
882
887
|
def status
|
883
|
-
@
|
888
|
+
@http_header.status_code
|
884
889
|
end
|
885
890
|
|
886
891
|
alias code status
|
@@ -889,32 +894,58 @@ module HTTP
|
|
889
894
|
# Sets HTTP status code of response. Integer.
|
890
895
|
# Reason phrase is updated, too.
|
891
896
|
def status=(status)
|
892
|
-
@
|
897
|
+
@http_header.status_code = status
|
893
898
|
end
|
894
899
|
|
895
900
|
# Returns HTTP status reason phrase in response. String.
|
896
901
|
def reason
|
897
|
-
@
|
902
|
+
@http_header.reason_phrase
|
898
903
|
end
|
899
904
|
|
900
905
|
# Sets HTTP status reason phrase of response. String.
|
901
906
|
def reason=(reason)
|
902
|
-
@
|
907
|
+
@http_header.reason_phrase = reason
|
903
908
|
end
|
904
909
|
|
905
910
|
# Sets 'Content-Type' header value. Overrides if already exists.
|
906
911
|
def contenttype
|
907
|
-
@
|
912
|
+
@http_header.contenttype
|
908
913
|
end
|
909
914
|
|
910
915
|
# Returns 'Content-Type' header value.
|
911
916
|
def contenttype=(contenttype)
|
912
|
-
@
|
917
|
+
@http_header.contenttype = contenttype
|
913
918
|
end
|
914
919
|
|
915
920
|
# Returns a content of message body. A String or an IO.
|
916
921
|
def content
|
917
|
-
@
|
922
|
+
@http_body.content
|
923
|
+
end
|
924
|
+
|
925
|
+
alias header http_header
|
926
|
+
alias body content
|
927
|
+
|
928
|
+
# Returns Hash of header. key and value are both String. Each key has a
|
929
|
+
# single value so you can't extract exact value when a message has multiple
|
930
|
+
# headers like 'Set-Cookie'. Use header['Set-Cookie'] for that purpose.
|
931
|
+
# (It returns an Array always)
|
932
|
+
def headers
|
933
|
+
Hash[http_header.all]
|
934
|
+
end
|
935
|
+
|
936
|
+
# Extracts cookies from 'Set-Cookie' header.
|
937
|
+
# Supports 'Set-Cookie' in response header only.
|
938
|
+
# Do we need 'Cookie' support in request header?
|
939
|
+
def cookies
|
940
|
+
set_cookies = http_header['set-cookie']
|
941
|
+
unless set_cookies.empty?
|
942
|
+
uri = http_header.request_uri
|
943
|
+
set_cookies.map { |str|
|
944
|
+
cookie = WebAgent::Cookie.new
|
945
|
+
cookie.parse(str, uri)
|
946
|
+
cookie
|
947
|
+
}
|
948
|
+
end
|
918
949
|
end
|
919
950
|
end
|
920
951
|
|
data/lib/httpclient/session.rb
CHANGED
@@ -152,7 +152,7 @@ class HTTPClient
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def query(req, via_proxy)
|
155
|
-
req.
|
155
|
+
req.http_body.chunk_size = @chunk_size
|
156
156
|
sess = open(req.header.request_uri, via_proxy)
|
157
157
|
begin
|
158
158
|
sess.query(req)
|
@@ -812,7 +812,7 @@ class HTTPClient
|
|
812
812
|
close
|
813
813
|
end
|
814
814
|
end
|
815
|
-
@next_connection = false
|
815
|
+
@next_connection = false if !@content_length and !@chunked
|
816
816
|
end
|
817
817
|
|
818
818
|
StatusParseRegexp = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?\r?\n\z)
|
@@ -21,7 +21,7 @@ class HTTPClient
|
|
21
21
|
# == Trust Anchor Control
|
22
22
|
#
|
23
23
|
# SSLConfig loads 'httpclient/cacert.p7s' as a trust anchor
|
24
|
-
# (trusted certificate(s)) with
|
24
|
+
# (trusted certificate(s)) with add_trust_ca in initialization time.
|
25
25
|
# This means that HTTPClient instance trusts some CA certificates by default,
|
26
26
|
# like Web browsers. 'httpclient/cacert.p7s' is created by the author and
|
27
27
|
# included in released package.
|
@@ -29,7 +29,7 @@ class HTTPClient
|
|
29
29
|
# 'cacert.p7s' is automatically generated from JDK 1.6.
|
30
30
|
#
|
31
31
|
# You may want to change trust anchor by yourself. Call clear_cert_store
|
32
|
-
# then
|
32
|
+
# then add_trust_ca for that purpose.
|
33
33
|
class SSLConfig
|
34
34
|
include OpenSSL if SSLEnabled
|
35
35
|
|
@@ -82,7 +82,7 @@ class HTTPClient
|
|
82
82
|
@timeout = nil
|
83
83
|
@options = defined?(SSL::OP_ALL) ? SSL::OP_ALL | SSL::OP_NO_SSLv2 : nil
|
84
84
|
@ciphers = "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
|
85
|
-
|
85
|
+
@cacerts_loaded = false
|
86
86
|
end
|
87
87
|
|
88
88
|
# Sets certificate (OpenSSL::X509::Certificate) for SSL client
|
@@ -122,6 +122,7 @@ class HTTPClient
|
|
122
122
|
#
|
123
123
|
# Calling this method resets all existing sessions.
|
124
124
|
def clear_cert_store
|
125
|
+
@cacerts_loaded = true # avoid lazy override
|
125
126
|
@cert_store = X509::Store.new
|
126
127
|
change_notify
|
127
128
|
end
|
@@ -131,6 +132,7 @@ class HTTPClient
|
|
131
132
|
#
|
132
133
|
# Calling this method resets all existing sessions.
|
133
134
|
def cert_store=(cert_store)
|
135
|
+
@cacerts_loaded = true # avoid lazy override
|
134
136
|
@cert_store = cert_store
|
135
137
|
change_notify
|
136
138
|
end
|
@@ -142,7 +144,8 @@ class HTTPClient
|
|
142
144
|
# trusted certificate files.
|
143
145
|
#
|
144
146
|
# Calling this method resets all existing sessions.
|
145
|
-
def
|
147
|
+
def add_trust_ca(trust_ca_file_or_hashed_dir)
|
148
|
+
@cacerts_loaded = true # avoid lazy override
|
146
149
|
if FileTest.directory?(trust_ca_file_or_hashed_dir)
|
147
150
|
@cert_store.add_path(trust_ca_file_or_hashed_dir)
|
148
151
|
else
|
@@ -150,13 +153,21 @@ class HTTPClient
|
|
150
153
|
end
|
151
154
|
change_notify
|
152
155
|
end
|
156
|
+
alias set_trust_ca add_trust_ca
|
157
|
+
|
158
|
+
# Loads default trust anchors.
|
159
|
+
# Calling this method resets all existing sessions.
|
160
|
+
def load_trust_ca
|
161
|
+
load_cacerts
|
162
|
+
change_notify
|
163
|
+
end
|
153
164
|
|
154
165
|
# Adds CRL for verification.
|
155
166
|
# crl:: a OpenSSL::X509::CRL or a filename of a PEM/DER formatted
|
156
167
|
# OpenSSL::X509::CRL.
|
157
168
|
#
|
158
169
|
# Calling this method resets all existing sessions.
|
159
|
-
def
|
170
|
+
def add_crl(crl)
|
160
171
|
unless crl.is_a?(X509::CRL)
|
161
172
|
crl = X509::CRL.new(File.open(crl) { |f| f.read })
|
162
173
|
end
|
@@ -164,6 +175,7 @@ class HTTPClient
|
|
164
175
|
@cert_store.flags = X509::V_FLAG_CRL_CHECK | X509::V_FLAG_CRL_CHECK_ALL
|
165
176
|
change_notify
|
166
177
|
end
|
178
|
+
alias set_crl add_crl
|
167
179
|
|
168
180
|
# Sets verify mode of OpenSSL. New value must be a combination of
|
169
181
|
# constants OpenSSL::SSL::VERIFY_*
|
@@ -223,6 +235,8 @@ class HTTPClient
|
|
223
235
|
|
224
236
|
# interfaces for SSLSocketWrap.
|
225
237
|
def set_context(ctx) # :nodoc:
|
238
|
+
load_trust_ca unless @cacerts_loaded
|
239
|
+
@cacerts_loaded = true
|
226
240
|
# Verification: Use Store#verify_callback instead of SSLContext#verify*?
|
227
241
|
ctx.cert_store = @cert_store
|
228
242
|
ctx.verify_mode = @verify_mode
|
@@ -350,7 +364,7 @@ class HTTPClient
|
|
350
364
|
store = X509::Store.new
|
351
365
|
store.add_cert(selfcert)
|
352
366
|
if (p7.verify(nil, store, p7.data, 0))
|
353
|
-
|
367
|
+
add_trust_ca(file)
|
354
368
|
return
|
355
369
|
end
|
356
370
|
end
|
data/lib/httpclient/timeout.rb
CHANGED
@@ -23,6 +23,7 @@ class HTTPClient
|
|
23
23
|
# timeout scheduler.
|
24
24
|
# * Do not wakeup the scheduler thread so often. Let scheduler thread sleep
|
25
25
|
# until the nearest period.
|
26
|
+
if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
|
26
27
|
class TimeoutScheduler
|
27
28
|
|
28
29
|
# Represents timeout period.
|
@@ -117,6 +118,7 @@ class HTTPClient
|
|
117
118
|
end
|
118
119
|
end
|
119
120
|
timeout_scheduler # initialize at first time.
|
121
|
+
end
|
120
122
|
|
121
123
|
module Timeout
|
122
124
|
if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
|
data/lib/httpclient/util.rb
CHANGED
@@ -43,10 +43,34 @@ class HTTPClient
|
|
43
43
|
#
|
44
44
|
def keyword_argument(args, *field)
|
45
45
|
if args.size == 1 and args[0].is_a?(Hash)
|
46
|
-
args[0].values_at(*field)
|
47
|
-
|
48
|
-
|
46
|
+
r = args[0].values_at(*field)
|
47
|
+
unless r.compact.empty?
|
48
|
+
return r
|
49
|
+
end
|
50
|
+
end
|
51
|
+
args
|
52
|
+
end
|
53
|
+
|
54
|
+
# Keyword argument to hash helper.
|
55
|
+
# args:: given arguments.
|
56
|
+
# *field:: a list of arguments to be extracted.
|
57
|
+
#
|
58
|
+
# Returns hash which has defined keys. When a Hash given, returns it
|
59
|
+
# including undefined keys. When an Array given, returns a Hash which only
|
60
|
+
# includes defined keys.
|
61
|
+
def argument_to_hash(args, *field)
|
62
|
+
return nil if args.empty?
|
63
|
+
if args.size == 1 and args[0].is_a?(Hash)
|
64
|
+
r = args[0].values_at(*field)
|
65
|
+
unless r.compact.empty?
|
66
|
+
return args[0]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
h = {}
|
70
|
+
field.each_with_index do |e, idx|
|
71
|
+
h[e] = args[idx]
|
49
72
|
end
|
73
|
+
h
|
50
74
|
end
|
51
75
|
|
52
76
|
# Gets an URI instance.
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: httpclient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.
|
5
|
+
version: 2.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- NAKAMURA, Hiroshi
|
@@ -10,8 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-04-08 00:00:00 Z
|
15
14
|
dependencies: []
|
16
15
|
|
17
16
|
description:
|
@@ -49,7 +48,6 @@ files:
|
|
49
48
|
- lib/httpclient/util.rbc
|
50
49
|
- lib/httpclient/http.rb
|
51
50
|
- lib/httpclient.rb
|
52
|
-
has_rdoc: true
|
53
51
|
homepage: http://github.com/nahi/httpclient
|
54
52
|
licenses: []
|
55
53
|
|
@@ -73,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
71
|
requirements: []
|
74
72
|
|
75
73
|
rubyforge_project:
|
76
|
-
rubygems_version: 1.
|
74
|
+
rubygems_version: 1.7.1
|
77
75
|
signing_key:
|
78
76
|
specification_version: 3
|
79
77
|
summary: gives something like the functionality of libwww-perl (LWP) in Ruby
|