httpclient 2.2.7 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +61 -18
- data/bin/httpclient +65 -0
- data/lib/http-access2.rb +5 -3
- data/lib/httpclient.rb +25 -15
- data/lib/httpclient/cookie.rb +3 -3
- data/lib/httpclient/http.rb +39 -18
- data/lib/httpclient/session.rb +16 -14
- data/lib/httpclient/ssl_config.rb +1 -2
- data/lib/httpclient/util.rb +40 -5
- data/lib/httpclient/version.rb +1 -1
- data/test/test_cookie.rb +23 -16
- data/test/test_http-access2.rb +7 -4
- data/test/test_httpclient.rb +132 -34
- metadata +7 -5
data/README.txt
CHANGED
@@ -12,6 +12,7 @@ See HTTPClient for documentation.
|
|
12
12
|
* methods like GET/HEAD/POST/* via HTTP/1.1.
|
13
13
|
* HTTPS(SSL), Cookies, proxy, authentication(Digest, NTLM, Basic), etc.
|
14
14
|
* asynchronous HTTP request, streaming HTTP request.
|
15
|
+
* debug mode CLI.
|
15
16
|
|
16
17
|
* by contrast with net/http in standard distribution;
|
17
18
|
* Cookies support
|
@@ -29,6 +30,16 @@ See HTTPClient for documentation.
|
|
29
30
|
* Rather advanced HTTP/1.1 usage such as Range, deflate, etc.
|
30
31
|
(of course you can set it in header by yourself)
|
31
32
|
|
33
|
+
== httpclient command
|
34
|
+
|
35
|
+
Usage: 1) % httpclient get https://www.google.co.jp/ q=ruby
|
36
|
+
Usage: 2) % httpclient
|
37
|
+
|
38
|
+
For 1) it issues a GET request to the given URI and shows the wiredump and
|
39
|
+
the parsed result. For 2) it invokes irb shell with the binding that has a
|
40
|
+
HTTPClient as 'self'. You can call HTTPClient instance methods like;
|
41
|
+
|
42
|
+
> get "https://www.google.co.jp/", :q => :ruby
|
32
43
|
|
33
44
|
== Author
|
34
45
|
|
@@ -43,10 +54,11 @@ This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
|
43
54
|
redistribute it and/or modify it under the same terms of Ruby's license;
|
44
55
|
either the dual license version in 2003, or any later version.
|
45
56
|
|
46
|
-
httpclient/session.rb is based on http-access.rb in http-access/0.0.4.
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
httpclient/session.rb is based on http-access.rb in http-access/0.0.4. Some
|
58
|
+
part of it is copyrighted by Maebashi-san who made and published
|
59
|
+
http-access/0.0.4. http-access/0.0.4 did not include license notice but when
|
60
|
+
I asked Maebashi-san he agreed that I can redistribute it under the same terms
|
61
|
+
of Ruby. Many thanks to Maebashi-san.
|
50
62
|
|
51
63
|
|
52
64
|
== Install
|
@@ -95,6 +107,37 @@ Thanks in advance.
|
|
95
107
|
|
96
108
|
== Changes
|
97
109
|
|
110
|
+
= Changes in 2.3.0 =
|
111
|
+
|
112
|
+
October 10, 2012 - version 2.3.0
|
113
|
+
|
114
|
+
* Features
|
115
|
+
|
116
|
+
* Added debug mode CLI. bin/httpclient is installed as CLI.
|
117
|
+
Usage: 1) % httpclient get https://www.google.co.jp/ q=ruby
|
118
|
+
Usage: 2) %httpclient
|
119
|
+
For 1) it issues a GET request to the given URI and shows the wiredump
|
120
|
+
and the parsed result. For 2) it invokes irb shell with the binding
|
121
|
+
that has a HTTPClient as 'self'. You can call HTTPClient instance
|
122
|
+
methods like;
|
123
|
+
> get "https://www.google.co.jp/", :q => :ruby
|
124
|
+
|
125
|
+
* #119 Addressable gem support (only if it exists); should handle IRI
|
126
|
+
properly.
|
127
|
+
|
128
|
+
* Bug fixes
|
129
|
+
|
130
|
+
* #115 Cookies couldn't work properly if the path in an URI is ommited.
|
131
|
+
* #112, #117 Proper handling of sized IO (the IO object that responds to
|
132
|
+
:size) for chunked POST. HTTPClient did read till EOF even if the
|
133
|
+
given IO has :size method.
|
134
|
+
* Handle '303 See Other' properly. RFC2616 says it should be redirected
|
135
|
+
with GET.
|
136
|
+
* #116 Fix "100-continue" support. It was just ignored.
|
137
|
+
* #118 Support for boolean values when making POST/PUT requests with
|
138
|
+
multiipart/form Content-Type.
|
139
|
+
* #110 Allows leading dots in no_proxy hostname suffixes.
|
140
|
+
|
98
141
|
= Changes in 2.2.7 =
|
99
142
|
|
100
143
|
August 14, 2012 - version 2.2.7
|
@@ -102,7 +145,7 @@ Thanks in advance.
|
|
102
145
|
* Bug fixes
|
103
146
|
|
104
147
|
* Fix arity incompatibility introduced in 2.2.6. It broke Webmock.
|
105
|
-
|
148
|
+
Thanks Andrew France for the report!
|
106
149
|
|
107
150
|
= Changes in 2.2.6 =
|
108
151
|
|
@@ -111,29 +154,29 @@ Thanks in advance.
|
|
111
154
|
* Bug fixes
|
112
155
|
|
113
156
|
* Make get_content doesn't raise a BadResponseError for perfectly good
|
114
|
-
|
157
|
+
responses like 304 Not Modified. Thanks to Florian Hars.
|
115
158
|
|
116
159
|
* Add 'Content-Type: application/x-www-form-urlencoded' for the PUT
|
117
|
-
|
160
|
+
request that has urlencoded entity-body.
|
118
161
|
|
119
162
|
* Features
|
120
163
|
|
121
164
|
* Add HTTPClient::IncludeClient by Jonathan Rochkind, a mix-in for easily
|
122
|
-
|
123
|
-
|
165
|
+
adding a thread-safe lazily initialized class-level HTTPClient object
|
166
|
+
to your class.
|
124
167
|
|
125
168
|
* Proxy DigestAuth support. Thanks to Alexander Kotov and Florian Hars.
|
126
169
|
|
127
170
|
* Accept an array of strings (and IO-likes) as a query value
|
128
|
-
|
129
|
-
|
171
|
+
e.g. `{ x: 'a', y: [1,2,3] }` is encoded into `"x=a&y=1&y=2&y=3"`.
|
172
|
+
Thanks to Akinori MUSHA.
|
130
173
|
|
131
174
|
* Allow body for DELETE method.
|
132
175
|
|
133
176
|
* Allow :follow_redirect => true for HEAD request.
|
134
177
|
|
135
178
|
* Fill request parameters request_method, request_uri and request_query
|
136
|
-
|
179
|
+
as part of response Message::Header.
|
137
180
|
|
138
181
|
= Changes in 2.2.5 =
|
139
182
|
|
@@ -143,8 +186,8 @@ Thanks in advance.
|
|
143
186
|
|
144
187
|
* Added Magic encoding comment to hexdump.rb to avoid encoding error.
|
145
188
|
* Add workaround for JRuby issue on Windows (JRUBY-6136)
|
146
|
-
|
147
|
-
|
189
|
+
On Windows, calling File#size fails with an Unknown error (20047).
|
190
|
+
This workaround uses File#lstat instead.
|
148
191
|
* Require open-uri only on ruby 1.9, since it is not needed on 1.8.
|
149
192
|
|
150
193
|
* Features
|
@@ -153,11 +196,11 @@ Thanks in advance.
|
|
153
196
|
* Dump more SSL certificate information under $DEBUG.
|
154
197
|
* Add HTTPClient::SSLConfig#ssl_version property.
|
155
198
|
* Add 'Accept: */*' header to request by default. Rails requies it.
|
156
|
-
|
199
|
+
It doesn't override given Accept header from API.
|
157
200
|
* Add HTTPClient::SSLConfig#set_default_paths. This method makes
|
158
|
-
|
201
|
+
HTTPClient instance to use OpenSSL's default trusted CA certificates.
|
159
202
|
* Allow to set Date header manually.
|
160
|
-
|
203
|
+
ex. clent.get(uri, :header => {'Date' => Time.now.httpdate})
|
161
204
|
|
162
205
|
= Changes in 2.2.4 =
|
163
206
|
|
@@ -665,7 +708,7 @@ Thanks in advance.
|
|
665
708
|
[,:] separated. ("ruby-lang.org:rubyist.net")
|
666
709
|
No regexp. (give "ruby-lang.org", not "*.ruby-lang.org")
|
667
710
|
If you want specify hot by IP address, give full address.
|
668
|
-
|
711
|
+
("192.168.1.1, 192.168.1.2")
|
669
712
|
|
670
713
|
September 10, 2003 - version 2.0
|
671
714
|
CamelCase to non_camel_case.
|
data/bin/httpclient
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# httpclient shell command.
|
4
|
+
#
|
5
|
+
# Usage: 1) % httpclient get https://www.google.co.jp/ q=ruby
|
6
|
+
# Usage: 2) % httpclient
|
7
|
+
#
|
8
|
+
# For 1) it issues a GET request to the given URI and shows the wiredump and
|
9
|
+
# the parsed result. For 2) it invokes irb shell with the binding that has a
|
10
|
+
# HTTPClient as 'self'. You can call HTTPClient instance methods like;
|
11
|
+
# > get "https://www.google.co.jp/", :q => :ruby
|
12
|
+
require 'httpclient'
|
13
|
+
|
14
|
+
METHODS = ['head', 'get', 'post', 'put', 'delete', 'options', 'propfind', 'proppatch', 'trace']
|
15
|
+
if ARGV.size >= 2 && METHODS.include?(ARGV[0])
|
16
|
+
client = HTTPClient.new
|
17
|
+
client.debug_dev = STDERR
|
18
|
+
$DEBUG = true
|
19
|
+
require 'pp'
|
20
|
+
pp client.send(*ARGV)
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'irb'
|
25
|
+
require 'irb/completion'
|
26
|
+
|
27
|
+
class Runner
|
28
|
+
def initialize
|
29
|
+
@httpclient = HTTPClient.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(msg, *a, &b)
|
33
|
+
debug, $DEBUG = $DEBUG, true
|
34
|
+
begin
|
35
|
+
@httpclient.send(msg, *a, &b)
|
36
|
+
ensure
|
37
|
+
$DEBUG = debug
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run
|
42
|
+
IRB.setup(nil)
|
43
|
+
ws = IRB::WorkSpace.new(binding)
|
44
|
+
irb = IRB::Irb.new(ws)
|
45
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
46
|
+
|
47
|
+
trap("SIGINT") do
|
48
|
+
irb.signal_handle
|
49
|
+
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
catch(:IRB_EXIT) do
|
53
|
+
irb.eval_input
|
54
|
+
end
|
55
|
+
ensure
|
56
|
+
IRB.irb_at_exit
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
'HTTPClient'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Runner.new.run
|
data/lib/http-access2.rb
CHANGED
@@ -5,9 +5,11 @@
|
|
5
5
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
6
6
|
# either the dual license version in 2003, or any later version.
|
7
7
|
|
8
|
-
# http-access2.rb is based on http-access.rb in http-access/0.0.4. Some
|
9
|
-
# of
|
10
|
-
#
|
8
|
+
# http-access2.rb is based on http-access.rb in http-access/0.0.4. Some
|
9
|
+
# part of it is copyrighted by Maebashi-san who made and published
|
10
|
+
# http-access/0.0.4. http-access/0.0.4 did not include license notice but
|
11
|
+
# when I asked Maebashi-san he agreed that I can redistribute it under the
|
12
|
+
# same terms of Ruby. Many thanks to Maebashi-san.
|
11
13
|
|
12
14
|
|
13
15
|
require 'httpclient'
|
data/lib/httpclient.rb
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
# either the dual license version in 2003, or any later version.
|
7
7
|
|
8
8
|
|
9
|
-
require 'uri'
|
10
9
|
require 'stringio'
|
11
10
|
require 'digest/sha1'
|
12
11
|
|
@@ -378,6 +377,7 @@ class HTTPClient
|
|
378
377
|
proxy, agent_name, from = keyword_argument(args, :proxy, :agent_name, :from)
|
379
378
|
@proxy = nil # assigned later.
|
380
379
|
@no_proxy = nil
|
380
|
+
@no_proxy_regexps = []
|
381
381
|
@www_auth = WWWAuth.new
|
382
382
|
@proxy_auth = ProxyAuth.new
|
383
383
|
@request_filter = [@proxy_auth, @www_auth]
|
@@ -478,6 +478,17 @@ class HTTPClient
|
|
478
478
|
# Calling this method resets all existing sessions.
|
479
479
|
def no_proxy=(no_proxy)
|
480
480
|
@no_proxy = no_proxy
|
481
|
+
@no_proxy_regexps.clear
|
482
|
+
if @no_proxy
|
483
|
+
@no_proxy.scan(/([^:,]+)(?::(\d+))?/) do |host, port|
|
484
|
+
if host[0] == ?.
|
485
|
+
regexp = /#{Regexp.quote(host)}\z/i
|
486
|
+
else
|
487
|
+
regexp = /(\A|\.)#{Regexp.quote(host)}\z/i
|
488
|
+
end
|
489
|
+
@no_proxy_regexps << [regexp, port]
|
490
|
+
end
|
491
|
+
end
|
481
492
|
reset_all
|
482
493
|
end
|
483
494
|
|
@@ -623,11 +634,11 @@ class HTTPClient
|
|
623
634
|
# Location: ../foo/
|
624
635
|
# in HTTP header. (raises BadResponseError instead)
|
625
636
|
def strict_redirect_uri_callback(uri, res)
|
626
|
-
newuri =
|
637
|
+
newuri = urify(res.header['location'][0])
|
627
638
|
if https?(uri) && !https?(newuri)
|
628
639
|
raise BadResponseError.new("redirecting to non-https resource")
|
629
640
|
end
|
630
|
-
|
641
|
+
if !http?(newuri) && !https?(newuri)
|
631
642
|
raise BadResponseError.new("unexpected location: #{newuri}", res)
|
632
643
|
end
|
633
644
|
puts "redirect to: #{newuri}" if $DEBUG
|
@@ -640,8 +651,8 @@ class HTTPClient
|
|
640
651
|
# Location: ../foo/
|
641
652
|
# in HTTP header.
|
642
653
|
def default_redirect_uri_callback(uri, res)
|
643
|
-
newuri =
|
644
|
-
|
654
|
+
newuri = urify(res.header['location'][0])
|
655
|
+
if !http?(newuri) && !https?(newuri)
|
645
656
|
newuri = uri + newuri
|
646
657
|
warn("could be a relative URI in location header which is not recommended")
|
647
658
|
warn("'The field value consists of a single absolute URI' in HTTP spec")
|
@@ -933,7 +944,7 @@ private
|
|
933
944
|
uri = urify(uri)
|
934
945
|
if block
|
935
946
|
filtered_block = proc { |r, str|
|
936
|
-
block.call(str) if
|
947
|
+
block.call(str) if r.ok?
|
937
948
|
}
|
938
949
|
end
|
939
950
|
if HTTP::Message.file?(body)
|
@@ -943,7 +954,8 @@ private
|
|
943
954
|
while retry_number < @follow_redirect_count
|
944
955
|
body.pos = pos if pos
|
945
956
|
res = do_request(method, uri, query, body, header, &filtered_block)
|
946
|
-
if
|
957
|
+
if res.redirect?
|
958
|
+
method = :get if res.see_other? # See RFC2616 10.3.4
|
947
959
|
uri = urify(@redirect_uri_callback.call(uri, res))
|
948
960
|
retry_number += 1
|
949
961
|
else
|
@@ -954,7 +966,7 @@ private
|
|
954
966
|
end
|
955
967
|
|
956
968
|
def success_content(res)
|
957
|
-
if
|
969
|
+
if res.ok?
|
958
970
|
return res.content
|
959
971
|
else
|
960
972
|
raise BadResponseError.new("unexpected response: #{res.header.inspect}", res)
|
@@ -1041,13 +1053,11 @@ private
|
|
1041
1053
|
if !@proxy or NO_PROXY_HOSTS.include?(uri.host)
|
1042
1054
|
return true
|
1043
1055
|
end
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
(!port || uri.port == port.to_i)
|
1050
|
-
return true
|
1056
|
+
@no_proxy_regexps.each do |regexp, port|
|
1057
|
+
if !port || uri.port == port.to_i
|
1058
|
+
if regexp =~ uri.host
|
1059
|
+
return true
|
1060
|
+
end
|
1051
1061
|
end
|
1052
1062
|
end
|
1053
1063
|
false
|
data/lib/httpclient/cookie.rb
CHANGED
@@ -11,9 +11,9 @@
|
|
11
11
|
#
|
12
12
|
# w3m homepage: http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/
|
13
13
|
|
14
|
-
require 'uri'
|
15
14
|
require 'time'
|
16
15
|
require 'monitor'
|
16
|
+
require 'httpclient/util'
|
17
17
|
|
18
18
|
class WebAgent
|
19
19
|
|
@@ -133,7 +133,7 @@ class WebAgent
|
|
133
133
|
domainname = url.host
|
134
134
|
if (!domainname ||
|
135
135
|
!domain_match(domainname, @domain) ||
|
136
|
-
(@path && !head_match?(@path, url.path)) ||
|
136
|
+
(@path && !head_match?(@path, url.path.empty? ? '/' : url.path)) ||
|
137
137
|
(@secure && (url.scheme != 'https')) )
|
138
138
|
return false
|
139
139
|
else
|
@@ -421,7 +421,7 @@ class WebAgent
|
|
421
421
|
cookie = WebAgent::Cookie.new()
|
422
422
|
@cookies << cookie
|
423
423
|
col = line.chomp.split(/\t/)
|
424
|
-
cookie.url =
|
424
|
+
cookie.url = HTTPClient::Util.urify(col[0])
|
425
425
|
cookie.name = col[1]
|
426
426
|
cookie.value = col[2]
|
427
427
|
if col[3].empty? or col[3] == '0'
|
data/lib/httpclient/http.rb
CHANGED
@@ -188,7 +188,7 @@ module HTTP
|
|
188
188
|
end
|
189
189
|
|
190
190
|
# Placeholder URI object for nil uri.
|
191
|
-
NIL_URI =
|
191
|
+
NIL_URI = HTTPClient::Util.urify('http://nil-uri-given/')
|
192
192
|
# Initialize this instance as a general request.
|
193
193
|
def init_request(method, uri, query = nil)
|
194
194
|
@is_request = true
|
@@ -542,7 +542,7 @@ module HTTP
|
|
542
542
|
# bear in mind that server may not support it. at least ruby's CGI doesn't.
|
543
543
|
@body = body
|
544
544
|
remember_pos(@body)
|
545
|
-
@size = body.
|
545
|
+
@size = (body.size - body.pos) rescue body.size if body.respond_to?(:size)
|
546
546
|
elsif boundary and Message.multiparam_query?(body)
|
547
547
|
@body = build_query_multipart_str(body, boundary)
|
548
548
|
@size = @body.size
|
@@ -554,27 +554,45 @@ module HTTP
|
|
554
554
|
|
555
555
|
def remember_pos(io)
|
556
556
|
# IO may not support it (ex. IO.pipe)
|
557
|
-
@positions[io] = io.pos if io.respond_to?(:pos)
|
557
|
+
@positions[io] = io.pos rescue nil if io.respond_to?(:pos)
|
558
558
|
end
|
559
559
|
|
560
560
|
def reset_pos(io)
|
561
|
-
io.pos = @positions[io] if @positions
|
561
|
+
io.pos = @positions[io] if @positions[io]
|
562
562
|
end
|
563
563
|
|
564
564
|
def dump_file(io, dev)
|
565
|
+
return dump_file_length(io, @size, dev) if @size
|
565
566
|
buf = ''
|
566
567
|
while !io.read(@chunk_size, buf).nil?
|
567
568
|
dev << buf
|
568
569
|
end
|
569
570
|
end
|
570
571
|
|
572
|
+
def dump_file_length(io, size, dev)
|
573
|
+
buf = ''
|
574
|
+
while size > 0 && !(read = io.read(size > @chunk_size ? @chunk_size : size, buf)).nil?
|
575
|
+
dev << buf
|
576
|
+
size -= read.bytesize
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
571
580
|
def dump_chunks(io, dev)
|
581
|
+
return dump_chunks_length(io, io.size, dev) if io.respond_to?(:size) && io.size
|
572
582
|
buf = ''
|
573
583
|
while !io.read(@chunk_size, buf).nil?
|
574
584
|
dev << dump_chunk(buf)
|
575
585
|
end
|
576
586
|
end
|
577
587
|
|
588
|
+
def dump_chunks_length(io, size, dev)
|
589
|
+
buf = ''
|
590
|
+
while size > 0 && !(read = io.read(size > @chunk_size ? @chunk_size : size, buf)).nil?
|
591
|
+
dev << dump_chunk(buf)
|
592
|
+
size -= read.bytesize
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
578
596
|
def dump_chunk(str)
|
579
597
|
dump_chunk_size(str.bytesize) + (str + CRLF)
|
580
598
|
end
|
@@ -600,18 +618,8 @@ module HTTP
|
|
600
618
|
if Message.file?(part)
|
601
619
|
@as_stream = true
|
602
620
|
@body << part
|
603
|
-
|
604
|
-
|
605
|
-
elsif part.respond_to?(:size)
|
606
|
-
if sz = part.size
|
607
|
-
@size += sz
|
608
|
-
else
|
609
|
-
@size = nil
|
610
|
-
end
|
611
|
-
else
|
612
|
-
# use chunked upload
|
613
|
-
@size = nil
|
614
|
-
end
|
621
|
+
# use chunked upload
|
622
|
+
@size = nil
|
615
623
|
elsif @body[-1].is_a?(String)
|
616
624
|
@body[-1] += part.to_s
|
617
625
|
@size += part.to_s.bytesize if @size
|
@@ -633,7 +641,6 @@ module HTTP
|
|
633
641
|
def build_query_multipart_str(query, boundary)
|
634
642
|
parts = Parts.new
|
635
643
|
query.each do |attr, value|
|
636
|
-
value ||= ''
|
637
644
|
headers = ["--#{boundary}"]
|
638
645
|
if Message.file?(value)
|
639
646
|
remember_pos(value)
|
@@ -659,6 +666,7 @@ module HTTP
|
|
659
666
|
remember_pos(value) if Message.file?(value)
|
660
667
|
else
|
661
668
|
headers << %{Content-Disposition: form-data; name="#{attr}"}
|
669
|
+
value = value.to_s
|
662
670
|
end
|
663
671
|
parts.add(headers.join(CRLF) + CRLF + CRLF)
|
664
672
|
parts.add(value)
|
@@ -1027,7 +1035,20 @@ module HTTP
|
|
1027
1035
|
}
|
1028
1036
|
end
|
1029
1037
|
end
|
1030
|
-
end
|
1031
1038
|
|
1039
|
+
# Convenience method to return boolean of whether we had a successful request
|
1040
|
+
def ok?
|
1041
|
+
HTTP::Status.successful?(status)
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
def redirect?
|
1045
|
+
HTTP::Status.redirect?(status)
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
# SEE_OTHER is a redirect, but it should sent as GET
|
1049
|
+
def see_other?
|
1050
|
+
status == HTTP::Status::SEE_OTHER
|
1051
|
+
end
|
1052
|
+
end
|
1032
1053
|
|
1033
1054
|
end
|
data/lib/httpclient/session.rb
CHANGED
@@ -4,10 +4,12 @@
|
|
4
4
|
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
5
5
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
6
6
|
# either the dual license version in 2003, or any later version.
|
7
|
-
|
8
|
-
# httpclient/session.rb is based on http-access.rb in http-access/0.0.4.
|
9
|
-
#
|
10
|
-
#
|
7
|
+
#
|
8
|
+
# httpclient/session.rb is based on http-access.rb in http-access/0.0.4. Some
|
9
|
+
# part of it is copyrighted by Maebashi-san who made and published
|
10
|
+
# http-access/0.0.4. http-access/0.0.4 did not include license notice but when
|
11
|
+
# I asked Maebashi-san he agreed that I can redistribute it under the same terms
|
12
|
+
# of Ruby. Many thanks to Maebashi-san.
|
11
13
|
|
12
14
|
|
13
15
|
require 'socket'
|
@@ -749,10 +751,10 @@ class HTTPClient
|
|
749
751
|
@socket = create_socket(site)
|
750
752
|
if https?(@dest)
|
751
753
|
if @socket.is_a?(LoopBackSocket)
|
752
|
-
connect_ssl_proxy(@socket,
|
754
|
+
connect_ssl_proxy(@socket, urify(@dest.to_s)) if @proxy
|
753
755
|
else
|
754
756
|
@socket = create_ssl_socket(@socket)
|
755
|
-
connect_ssl_proxy(@socket,
|
757
|
+
connect_ssl_proxy(@socket, urify(@dest.to_s)) if @proxy
|
756
758
|
begin
|
757
759
|
@socket.ssl_connect(@dest.host)
|
758
760
|
ensure
|
@@ -872,17 +874,17 @@ class HTTPClient
|
|
872
874
|
timeout(@receive_timeout, ReceiveTimeoutError) do
|
873
875
|
initial_line = nil
|
874
876
|
begin
|
875
|
-
|
876
|
-
|
877
|
+
begin
|
878
|
+
initial_line = @socket.gets("\n")
|
879
|
+
if initial_line.nil?
|
880
|
+
close
|
881
|
+
raise KeepAliveDisconnected.new(self)
|
882
|
+
end
|
883
|
+
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
|
884
|
+
# JRuby can raise IOError instead of ECONNRESET for now
|
877
885
|
close
|
878
886
|
raise KeepAliveDisconnected.new(self)
|
879
887
|
end
|
880
|
-
rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError
|
881
|
-
# JRuby can raise IOError instead of ECONNRESET for now
|
882
|
-
close
|
883
|
-
raise KeepAliveDisconnected.new(self)
|
884
|
-
end
|
885
|
-
begin
|
886
888
|
if StatusParseRegexp !~ initial_line
|
887
889
|
@version = '0.9'
|
888
890
|
@status = nil
|
@@ -82,8 +82,7 @@ class HTTPClient
|
|
82
82
|
@verify_callback = nil
|
83
83
|
@dest = nil
|
84
84
|
@timeout = nil
|
85
|
-
|
86
|
-
@ssl_version = "SSLv23"
|
85
|
+
@ssl_version = "SSLv3"
|
87
86
|
@options = defined?(SSL::OP_ALL) ? SSL::OP_ALL | SSL::OP_NO_SSLv2 : nil
|
88
87
|
# OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH"
|
89
88
|
@ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default
|
data/lib/httpclient/util.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
# HTTPClient - HTTP client library.
|
2
|
-
# Copyright (C) 2000-
|
2
|
+
# Copyright (C) 2000-2012 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
3
3
|
#
|
4
4
|
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
5
5
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
6
6
|
# either the dual license version in 2003, or any later version.
|
7
7
|
|
8
8
|
|
9
|
-
require 'uri'
|
10
|
-
|
11
|
-
|
12
9
|
unless ''.respond_to?(:bytesize)
|
13
10
|
class String
|
14
11
|
alias bytesize size
|
@@ -21,6 +18,37 @@ class HTTPClient
|
|
21
18
|
|
22
19
|
# A module for common function.
|
23
20
|
module Util
|
21
|
+
|
22
|
+
# URI abstraction; Addressable::URI or URI
|
23
|
+
require 'uri'
|
24
|
+
begin
|
25
|
+
require 'addressable/uri'
|
26
|
+
class AddressableURI < Addressable::URI
|
27
|
+
# Overwrites the original definition just for one line...
|
28
|
+
def authority
|
29
|
+
self.host && @authority ||= (begin
|
30
|
+
authority = ""
|
31
|
+
if self.userinfo != nil
|
32
|
+
authority << "#{self.userinfo}@"
|
33
|
+
end
|
34
|
+
authority << self.host
|
35
|
+
if self.port != self.default_port # ...HERE! Compares with default_port because self.port is not nil in this wrapper.
|
36
|
+
authority << ":#{self.port}"
|
37
|
+
end
|
38
|
+
authority
|
39
|
+
end)
|
40
|
+
end
|
41
|
+
|
42
|
+
# HTTPClient expects urify("http://foo/").port to be not nil but 80 like URI.
|
43
|
+
def port
|
44
|
+
super || default_port
|
45
|
+
end
|
46
|
+
end
|
47
|
+
AddressableEnabled = true
|
48
|
+
rescue LoadError
|
49
|
+
AddressableEnabled = false
|
50
|
+
end
|
51
|
+
|
24
52
|
# Keyword argument helper.
|
25
53
|
# args:: given arguments.
|
26
54
|
# *field:: a list of arguments to be extracted.
|
@@ -79,10 +107,13 @@ class HTTPClient
|
|
79
107
|
nil
|
80
108
|
elsif uri.is_a?(URI)
|
81
109
|
uri
|
110
|
+
elsif AddressableEnabled
|
111
|
+
AddressableURI.parse(uri.to_s)
|
82
112
|
else
|
83
113
|
URI.parse(uri.to_s)
|
84
114
|
end
|
85
115
|
end
|
116
|
+
module_function :urify
|
86
117
|
|
87
118
|
# Returns true if the given 2 URIs have a part_of relationship.
|
88
119
|
# * the same scheme
|
@@ -114,7 +145,11 @@ class HTTPClient
|
|
114
145
|
|
115
146
|
# Checks if the given URI is https.
|
116
147
|
def https?(uri)
|
117
|
-
uri.scheme.downcase == 'https'
|
148
|
+
uri.scheme && uri.scheme.downcase == 'https'
|
149
|
+
end
|
150
|
+
|
151
|
+
def http?(uri)
|
152
|
+
uri.scheme && uri.scheme.downcase == 'http'
|
118
153
|
end
|
119
154
|
end
|
120
155
|
|
data/lib/httpclient/version.rb
CHANGED
data/test/test_cookie.rb
CHANGED
@@ -4,6 +4,7 @@ require 'uri'
|
|
4
4
|
require 'httpclient/cookie'
|
5
5
|
|
6
6
|
class TestCookie < Test::Unit::TestCase
|
7
|
+
include HTTPClient::Util
|
7
8
|
|
8
9
|
def setup()
|
9
10
|
@c = WebAgent::Cookie.new()
|
@@ -20,7 +21,7 @@ class TestCookie < Test::Unit::TestCase
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def test_match()
|
23
|
-
url =
|
24
|
+
url = urify('http://www.rubycolor.org/hoge/funi/#919191')
|
24
25
|
|
25
26
|
@c.domain = 'www.rubycolor.org'
|
26
27
|
assert_equal(true, @c.match?(url))
|
@@ -51,7 +52,7 @@ class TestCookie < Test::Unit::TestCase
|
|
51
52
|
@c.secure = true
|
52
53
|
assert_equal(false, @c.match?(url))
|
53
54
|
|
54
|
-
url2 =
|
55
|
+
url2 = urify('https://www.rubycolor.org/hoge/funi/#919191')
|
55
56
|
@c.domain = 'www.rubycolor.org'
|
56
57
|
@c.path = '/hoge'
|
57
58
|
@c.secure = true
|
@@ -68,6 +69,11 @@ class TestCookie < Test::Unit::TestCase
|
|
68
69
|
# @c.port = [80,8080]
|
69
70
|
assert_equal(true, @c.match?(url))
|
70
71
|
|
72
|
+
url_nopath = URI.parse('http://www.rubycolor.org')
|
73
|
+
@c.domain = 'www.rubycolor.org'
|
74
|
+
@c.path = '/'
|
75
|
+
assert_equal(true, @c.match?(url_nopath))
|
76
|
+
|
71
77
|
end
|
72
78
|
|
73
79
|
def test_head_match?()
|
@@ -123,6 +129,7 @@ class TestCookie < Test::Unit::TestCase
|
|
123
129
|
end
|
124
130
|
|
125
131
|
class TestCookieManager < Test::Unit::TestCase
|
132
|
+
include HTTPClient::Util
|
126
133
|
|
127
134
|
def setup()
|
128
135
|
@cm = WebAgent::CookieManager.new()
|
@@ -147,7 +154,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
147
154
|
|
148
155
|
def test_parse()
|
149
156
|
str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; expires=Wed, 01-Dec-2010 00:00:00 GMT; path=/"
|
150
|
-
@cm.parse(str,
|
157
|
+
@cm.parse(str, urify('http://www.test.jp'))
|
151
158
|
cookie = @cm.cookies[0]
|
152
159
|
assert_instance_of(WebAgent::Cookie, cookie)
|
153
160
|
assert_equal("inkid", cookie.name)
|
@@ -158,7 +165,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
158
165
|
|
159
166
|
def test_parse2()
|
160
167
|
str = "xmen=off,0,0,1; path=/; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
|
161
|
-
@cm.parse(str,
|
168
|
+
@cm.parse(str, urify('http://www.excite.co.jp'))
|
162
169
|
cookie = @cm.cookies[0]
|
163
170
|
assert_instance_of(WebAgent::Cookie, cookie)
|
164
171
|
assert_equal("xmen", cookie.name)
|
@@ -170,7 +177,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
170
177
|
|
171
178
|
def test_parse3()
|
172
179
|
str = "xmen=off,0,0,1; path=/; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT;Secure;HTTPOnly"
|
173
|
-
@cm.parse(str,
|
180
|
+
@cm.parse(str, urify('http://www.excite.co.jp'))
|
174
181
|
cookie = @cm.cookies[0]
|
175
182
|
assert_instance_of(WebAgent::Cookie, cookie)
|
176
183
|
assert_equal("xmen", cookie.name)
|
@@ -184,7 +191,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
184
191
|
|
185
192
|
def test_parse_double_semicolon()
|
186
193
|
str = "xmen=off,0,0,1;; path=\"/;;\"; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
|
187
|
-
@cm.parse(str,
|
194
|
+
@cm.parse(str, urify('http://www.excite.co.jp'))
|
188
195
|
cookie = @cm.cookies[0]
|
189
196
|
assert_instance_of(WebAgent::Cookie, cookie)
|
190
197
|
assert_equal("xmen", cookie.name)
|
@@ -218,7 +225,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
218
225
|
|
219
226
|
def test_parse_expires
|
220
227
|
str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; expires=; path=/"
|
221
|
-
@cm.parse(str,
|
228
|
+
@cm.parse(str, urify('http://www.test.jp'))
|
222
229
|
cookie = @cm.cookies[0]
|
223
230
|
assert_equal("inkid", cookie.name)
|
224
231
|
assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value)
|
@@ -226,7 +233,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
226
233
|
assert_equal("/", cookie.path)
|
227
234
|
#
|
228
235
|
str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; path=/; expires="
|
229
|
-
@cm.parse(str,
|
236
|
+
@cm.parse(str, urify('http://www.test.jp'))
|
230
237
|
cookie = @cm.cookies[0]
|
231
238
|
assert_equal("inkid", cookie.name)
|
232
239
|
assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value)
|
@@ -234,7 +241,7 @@ class TestCookieManager < Test::Unit::TestCase
|
|
234
241
|
assert_equal("/", cookie.path)
|
235
242
|
#
|
236
243
|
str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; path=/; expires=\"\""
|
237
|
-
@cm.parse(str,
|
244
|
+
@cm.parse(str, urify('http://www.test.jp'))
|
238
245
|
cookie = @cm.cookies[0]
|
239
246
|
assert_equal("inkid", cookie.name)
|
240
247
|
assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value)
|
@@ -244,15 +251,15 @@ class TestCookieManager < Test::Unit::TestCase
|
|
244
251
|
|
245
252
|
def test_find_cookie()
|
246
253
|
str = "xmen=off,0,0,1; path=/; domain=.excite2.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
|
247
|
-
@cm.parse(str,
|
254
|
+
@cm.parse(str, urify("http://www.excite2.co.jp/"))
|
248
255
|
|
249
256
|
str = "xmen=off,0,0,2; path=/; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT"
|
250
|
-
@cm.parse(str,
|
257
|
+
@cm.parse(str, urify("http://www.excite.co.jp/"))
|
251
258
|
|
252
259
|
@cm.cookies[0].use = true
|
253
260
|
@cm.cookies[1].use = true
|
254
261
|
|
255
|
-
url =
|
262
|
+
url = urify('http://www.excite.co.jp/hoge/funi/')
|
256
263
|
cookie_str = @cm.find(url)
|
257
264
|
assert_equal("xmen=off,0,0,2", cookie_str)
|
258
265
|
end
|
@@ -331,7 +338,7 @@ EOF
|
|
331
338
|
def test_not_saved_expired_cookies
|
332
339
|
begin
|
333
340
|
@cm.cookies_file = 'tmp_test.tmp'
|
334
|
-
uri =
|
341
|
+
uri = urify('http://www.example.org')
|
335
342
|
@cm.parse("foo=1; path=/", uri)
|
336
343
|
@cm.parse("bar=2; path=/; expires=", uri)
|
337
344
|
@cm.parse("baz=3; path=/; expires=\"\"", uri)
|
@@ -349,7 +356,7 @@ EOF
|
|
349
356
|
c = WebAgent::Cookie.new()
|
350
357
|
c.name = "hoge"
|
351
358
|
c.value = "funi"
|
352
|
-
c.url =
|
359
|
+
c.url = urify("http://www.inac.co.jp/hoge")
|
353
360
|
@cm.add(c)
|
354
361
|
c = @cm.cookies[0]
|
355
362
|
assert_equal('hoge', c.name)
|
@@ -362,14 +369,14 @@ EOF
|
|
362
369
|
c.name = "hoge"
|
363
370
|
c.value = "funi"
|
364
371
|
c.path = ''
|
365
|
-
c.url =
|
372
|
+
c.url = urify("http://www.inac.co.jp/hoge/hoge2/hoge3")
|
366
373
|
@cm.add(c)
|
367
374
|
#
|
368
375
|
c = WebAgent::Cookie.new()
|
369
376
|
c.name = "hoge"
|
370
377
|
c.value = "funi"
|
371
378
|
#c.path = '' NO path given -> same as URL
|
372
|
-
c.url =
|
379
|
+
c.url = urify("http://www.inac.co.jp/hoge/hoge2/hoge3")
|
373
380
|
@cm.add(c)
|
374
381
|
#
|
375
382
|
c1, c2 = @cm.cookies
|
data/test/test_http-access2.rb
CHANGED
@@ -7,6 +7,7 @@ module HTTPAccess2
|
|
7
7
|
|
8
8
|
class TestClient < Test::Unit::TestCase
|
9
9
|
include Helper
|
10
|
+
include HTTPClient::Util
|
10
11
|
|
11
12
|
def setup
|
12
13
|
super
|
@@ -23,7 +24,7 @@ class TestClient < Test::Unit::TestCase
|
|
23
24
|
escape_noproxy do
|
24
25
|
@proxyio.string = ""
|
25
26
|
@client = HTTPAccess2::Client.new(proxyurl)
|
26
|
-
assert_equal(
|
27
|
+
assert_equal(urify(proxyurl), @client.proxy)
|
27
28
|
assert_equal(200, @client.head(serverurl).status)
|
28
29
|
assert(!@proxyio.string.empty?)
|
29
30
|
end
|
@@ -114,14 +115,16 @@ class TestClient < Test::Unit::TestCase
|
|
114
115
|
def test_proxy
|
115
116
|
setup_proxyserver
|
116
117
|
escape_noproxy do
|
117
|
-
|
118
|
+
begin
|
118
119
|
@client.proxy = "http://"
|
120
|
+
rescue
|
121
|
+
assert_match(/InvalidURIError/, $!.class.to_s)
|
119
122
|
end
|
120
123
|
@client.proxy = ""
|
121
124
|
assert_nil(@client.proxy)
|
122
125
|
@client.proxy = "http://foo:1234"
|
123
|
-
assert_equal(
|
124
|
-
uri =
|
126
|
+
assert_equal(urify("http://foo:1234"), @client.proxy)
|
127
|
+
uri = urify("http://bar:2345")
|
125
128
|
@client.proxy = uri
|
126
129
|
assert_equal(uri, @client.proxy)
|
127
130
|
#
|
data/test/test_httpclient.rb
CHANGED
@@ -4,6 +4,7 @@ require File.expand_path('helper', File.dirname(__FILE__))
|
|
4
4
|
|
5
5
|
class TestHTTPClient < Test::Unit::TestCase
|
6
6
|
include Helper
|
7
|
+
include HTTPClient::Util
|
7
8
|
|
8
9
|
def setup
|
9
10
|
super
|
@@ -20,7 +21,7 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
20
21
|
escape_noproxy do
|
21
22
|
@proxyio.string = ""
|
22
23
|
@client = HTTPClient.new(proxyurl)
|
23
|
-
assert_equal(
|
24
|
+
assert_equal(urify(proxyurl), @client.proxy)
|
24
25
|
assert_equal(200, @client.head(serverurl).status)
|
25
26
|
assert(/accept/ =~ @proxyio.string)
|
26
27
|
end
|
@@ -174,14 +175,16 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
174
175
|
def test_proxy
|
175
176
|
setup_proxyserver
|
176
177
|
escape_noproxy do
|
177
|
-
|
178
|
+
begin
|
178
179
|
@client.proxy = "http://"
|
180
|
+
rescue
|
181
|
+
assert_match(/InvalidURIError/, $!.class.to_s)
|
179
182
|
end
|
180
183
|
@client.proxy = ""
|
181
184
|
assert_nil(@client.proxy)
|
182
185
|
@client.proxy = "http://admin:admin@foo:1234"
|
183
|
-
assert_equal(
|
184
|
-
uri =
|
186
|
+
assert_equal(urify("http://admin:admin@foo:1234"), @client.proxy)
|
187
|
+
uri = urify("http://bar:2345")
|
185
188
|
@client.proxy = uri
|
186
189
|
assert_equal(uri, @client.proxy)
|
187
190
|
#
|
@@ -220,7 +223,7 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
220
223
|
ENV['http_proxy'] = "http://admin:admin@foo:1234"
|
221
224
|
ENV['NO_PROXY'] = "foobar"
|
222
225
|
client = HTTPClient.new
|
223
|
-
assert_equal(
|
226
|
+
assert_equal(urify("http://admin:admin@foo:1234"), client.proxy)
|
224
227
|
assert_equal('foobar', client.no_proxy)
|
225
228
|
end
|
226
229
|
end
|
@@ -235,7 +238,7 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
235
238
|
assert_equal(nil, client.proxy)
|
236
239
|
ENV['CGI_HTTP_PROXY'] = "http://admin:admin@foo:1234"
|
237
240
|
client = HTTPClient.new
|
238
|
-
assert_equal(
|
241
|
+
assert_equal(urify("http://admin:admin@foo:1234"), client.proxy)
|
239
242
|
end
|
240
243
|
end
|
241
244
|
|
@@ -298,6 +301,36 @@ class TestHTTPClient < Test::Unit::TestCase
|
|
298
301
|
end
|
299
302
|
end
|
300
303
|
|
304
|
+
def test_no_proxy_with_initial_dot
|
305
|
+
@client.debug_dev = str = ""
|
306
|
+
@client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
|
307
|
+
@client.no_proxy = ''
|
308
|
+
@client.proxy = proxyurl
|
309
|
+
@client.head('http://www.foo.com')
|
310
|
+
assert(/CONNECT TO localhost/ =~ str, 'via proxy')
|
311
|
+
#
|
312
|
+
@client.debug_dev = str = ""
|
313
|
+
@client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
|
314
|
+
@client.no_proxy = '.foo.com'
|
315
|
+
@client.proxy = proxyurl
|
316
|
+
@client.head('http://www.foo.com')
|
317
|
+
assert(/CONNECT TO www.foo.com/ =~ str, 'no proxy because .foo.com matches with www.foo.com')
|
318
|
+
#
|
319
|
+
@client.debug_dev = str = ""
|
320
|
+
@client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
|
321
|
+
@client.no_proxy = '.foo.com'
|
322
|
+
@client.proxy = proxyurl
|
323
|
+
@client.head('http://foo.com')
|
324
|
+
assert(/CONNECT TO localhost/ =~ str, 'via proxy because .foo.com does not matche with foo.com')
|
325
|
+
#
|
326
|
+
@client.debug_dev = str = ""
|
327
|
+
@client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n"
|
328
|
+
@client.no_proxy = 'foo.com'
|
329
|
+
@client.proxy = proxyurl
|
330
|
+
@client.head('http://foo.com')
|
331
|
+
assert(/CONNECT TO foo.com/ =~ str, 'no proxy because foo.com matches with foo.com')
|
332
|
+
end
|
333
|
+
|
301
334
|
def test_cookie_update_while_authentication
|
302
335
|
escape_noproxy do
|
303
336
|
@client.test_loopback_http_response << <<EOS
|
@@ -397,18 +430,18 @@ EOS
|
|
397
430
|
|
398
431
|
def test_request_uri_in_response
|
399
432
|
@client.test_loopback_http_response << "HTTP/1.0 200 OK\ncontent-length: 100\n\nmessage body"
|
400
|
-
assert_equal(
|
433
|
+
assert_equal(urify('http://google.com/'), @client.get('http://google.com/').header.request_uri)
|
401
434
|
end
|
402
435
|
|
403
436
|
def test_request_uri_in_response_when_redirect
|
404
|
-
expected =
|
437
|
+
expected = urify(serverurl + 'hello')
|
405
438
|
assert_equal(expected, @client.get(serverurl + 'redirect1', :follow_redirect => true).header.request_uri)
|
406
439
|
assert_equal(expected, @client.get(serverurl + 'redirect2', :follow_redirect => true).header.request_uri)
|
407
440
|
end
|
408
441
|
|
409
442
|
def test_redirect_non_https
|
410
443
|
url = serverurl + 'redirect1'
|
411
|
-
https_url =
|
444
|
+
https_url = urify(url)
|
412
445
|
https_url.scheme = 'https'
|
413
446
|
#
|
414
447
|
redirect_to_http = "HTTP/1.0 302 OK\nLocation: #{url}\n\n"
|
@@ -449,6 +482,10 @@ EOS
|
|
449
482
|
end
|
450
483
|
end
|
451
484
|
|
485
|
+
def test_redirect_see_other
|
486
|
+
assert_equal('hello', @client.post_content(serverurl + 'redirect_see_other'))
|
487
|
+
end
|
488
|
+
|
452
489
|
def test_redirect_relative
|
453
490
|
@client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n"
|
454
491
|
silent do
|
@@ -470,7 +507,7 @@ EOS
|
|
470
507
|
|
471
508
|
def test_redirect_https_relative
|
472
509
|
url = serverurl + 'redirect1'
|
473
|
-
https_url =
|
510
|
+
https_url = urify(url)
|
474
511
|
https_url.scheme = 'https'
|
475
512
|
@client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: /foo\n\n"
|
476
513
|
@client.test_loopback_http_response << "HTTP/1.0 200 OK\n\nhello"
|
@@ -604,7 +641,7 @@ EOS
|
|
604
641
|
end
|
605
642
|
|
606
643
|
def test_head_follow_redirect
|
607
|
-
expected =
|
644
|
+
expected = urify(serverurl + 'hello')
|
608
645
|
assert_equal(expected, @client.head(serverurl + 'hello', :follow_redirect => true).header.request_uri)
|
609
646
|
assert_equal(expected, @client.head(serverurl + 'redirect1', :follow_redirect => true).header.request_uri)
|
610
647
|
assert_equal(expected, @client.head(serverurl + 'redirect2', :follow_redirect => true).header.request_uri)
|
@@ -685,6 +722,22 @@ EOS
|
|
685
722
|
assert_equal("post,--hello\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n2\r\n--hello\r\nContent-Disposition: form-data; name=\"3\"\r\n\r\n4\r\n--hello--\r\n\r\n", res.content)
|
686
723
|
end
|
687
724
|
|
725
|
+
def test_post_with_custom_multipart_and_boolean_params
|
726
|
+
param = [['boolean_true', true]]
|
727
|
+
ext = { 'content-type' => 'multipart/form-data' }
|
728
|
+
assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext)
|
729
|
+
res = @client.post(serverurl + 'servlet', param, ext)
|
730
|
+
assert_match(/Content-Disposition: form-data; name="boolean_true"\r\n\r\ntrue\r\n/, res.content)
|
731
|
+
#
|
732
|
+
param = [['boolean_false', false]]
|
733
|
+
res = @client.post(serverurl + 'servlet', param, ext)
|
734
|
+
assert_match(/Content-Disposition: form-data; name="boolean_false"\r\n\r\nfalse\r\n/, res.content)
|
735
|
+
#
|
736
|
+
param = [['nil', nil]]
|
737
|
+
res = @client.post(serverurl + 'servlet', param, ext)
|
738
|
+
assert_match(/Content-Disposition: form-data; name="nil"\r\n\r\n\r\n/, res.content)
|
739
|
+
end
|
740
|
+
|
688
741
|
def test_post_with_file
|
689
742
|
STDOUT.sync = true
|
690
743
|
File.open(__FILE__) do |file|
|
@@ -695,19 +748,6 @@ EOS
|
|
695
748
|
end
|
696
749
|
end
|
697
750
|
|
698
|
-
def test_post_with_file_without_size
|
699
|
-
STDOUT.sync = true
|
700
|
-
File.open(__FILE__) do |file|
|
701
|
-
def file.size
|
702
|
-
# Simulates some strange Windows behaviour
|
703
|
-
raise SystemCallError.new "Unknown Error (20047)"
|
704
|
-
end
|
705
|
-
assert_nothing_raised do
|
706
|
-
@client.post(serverurl + 'servlet', {1=>2, 3=>file})
|
707
|
-
end
|
708
|
-
end
|
709
|
-
end
|
710
|
-
|
711
751
|
def test_post_with_io # streaming, but not chunked
|
712
752
|
myio = StringIO.new("X" * (HTTP::Message::Body::DEFAULT_CHUNK_SIZE + 1))
|
713
753
|
def myio.read(*args)
|
@@ -724,7 +764,7 @@ EOS
|
|
724
764
|
assert_match(/\r\n2\r\n/m, res.content)
|
725
765
|
assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content)
|
726
766
|
assert_match(/\r\nContent-Length:/m, str.string)
|
727
|
-
assert_equal(
|
767
|
+
assert_equal(2, myio.called, 'should just read 2 times, no need to extra read for detecting EOF')
|
728
768
|
end
|
729
769
|
|
730
770
|
def test_post_with_io_nosize # streaming + chunked post
|
@@ -741,6 +781,26 @@ EOS
|
|
741
781
|
assert_match(/\r\nTransfer-Encoding: chunked\r\n/m, str.string)
|
742
782
|
end
|
743
783
|
|
784
|
+
def test_post_with_sized_io
|
785
|
+
myio = StringIO.new("45")
|
786
|
+
def myio.size
|
787
|
+
1
|
788
|
+
end
|
789
|
+
@client.debug_dev = str = StringIO.new
|
790
|
+
res = @client.post(serverurl + 'servlet', myio)
|
791
|
+
assert_match(/\r\n4\n/, str.string, 'should send "4" not "45"')
|
792
|
+
end
|
793
|
+
|
794
|
+
def test_post_with_sized_io_chunked
|
795
|
+
myio = StringIO.new("45")
|
796
|
+
def myio.size
|
797
|
+
1
|
798
|
+
end
|
799
|
+
@client.debug_dev = str = StringIO.new
|
800
|
+
res = @client.post(serverurl + 'servlet', {:file => myio})
|
801
|
+
assert_match(/\r\n4\r\n/, str.string, 'should send "4" not "45"')
|
802
|
+
end
|
803
|
+
|
744
804
|
def test_post_async
|
745
805
|
param = {'1'=>'2', '3'=>'4'}
|
746
806
|
conn = @client.post_async(serverurl + 'servlet', param)
|
@@ -1126,8 +1186,8 @@ EOS
|
|
1126
1186
|
extend HTTPClient::Util
|
1127
1187
|
assert_nil(urify(nil))
|
1128
1188
|
uri = 'http://foo'
|
1129
|
-
assert_equal(
|
1130
|
-
assert_equal(
|
1189
|
+
assert_equal(urify(uri), urify(uri))
|
1190
|
+
assert_equal(urify(uri), urify(urify(uri)))
|
1131
1191
|
end
|
1132
1192
|
|
1133
1193
|
def test_connection
|
@@ -1147,7 +1207,7 @@ EOS
|
|
1147
1207
|
site.inspect
|
1148
1208
|
end
|
1149
1209
|
#
|
1150
|
-
site = HTTPClient::Site.new(
|
1210
|
+
site = HTTPClient::Site.new(urify('http://localhost:12345/foo'))
|
1151
1211
|
assert_equal('http', site.scheme)
|
1152
1212
|
assert_equal('localhost', site.host)
|
1153
1213
|
assert_equal(12345, site.port)
|
@@ -1157,9 +1217,9 @@ EOS
|
|
1157
1217
|
site.inspect
|
1158
1218
|
end
|
1159
1219
|
#
|
1160
|
-
site1 = HTTPClient::Site.new(
|
1161
|
-
site2 = HTTPClient::Site.new(
|
1162
|
-
site3 = HTTPClient::Site.new(
|
1220
|
+
site1 = HTTPClient::Site.new(urify('http://localhost:12341/'))
|
1221
|
+
site2 = HTTPClient::Site.new(urify('http://localhost:12342/'))
|
1222
|
+
site3 = HTTPClient::Site.new(urify('http://localhost:12342/'))
|
1163
1223
|
assert(!(site1 == site2))
|
1164
1224
|
h = { site1 => 'site1', site2 => 'site2' }
|
1165
1225
|
h[site3] = 'site3'
|
@@ -1212,9 +1272,9 @@ EOS
|
|
1212
1272
|
end
|
1213
1273
|
|
1214
1274
|
def test_connect_request
|
1215
|
-
req = HTTP::Message.new_connect_request(
|
1275
|
+
req = HTTP::Message.new_connect_request(urify('https://foo/bar'))
|
1216
1276
|
assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump)
|
1217
|
-
req = HTTP::Message.new_connect_request(
|
1277
|
+
req = HTTP::Message.new_connect_request(urify('https://example.com/'))
|
1218
1278
|
assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump)
|
1219
1279
|
end
|
1220
1280
|
|
@@ -1313,6 +1373,17 @@ EOS
|
|
1313
1373
|
assert_equal('PART_NUMBER', res.cookies[1].name)
|
1314
1374
|
end
|
1315
1375
|
|
1376
|
+
def test_ok_response_success
|
1377
|
+
res = HTTP::Message.new_response('response')
|
1378
|
+
assert_equal(true, res.ok?)
|
1379
|
+
res.status = 404
|
1380
|
+
assert_equal(false, res.ok?)
|
1381
|
+
res.status = 500
|
1382
|
+
assert_equal(false, res.ok?)
|
1383
|
+
res.status = 302
|
1384
|
+
assert_equal(false, res.ok?)
|
1385
|
+
end
|
1386
|
+
|
1316
1387
|
if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9'
|
1317
1388
|
def test_timeout_scheduler
|
1318
1389
|
assert_equal('hello', @client.get_content(serverurl + 'hello'))
|
@@ -1483,6 +1554,16 @@ EOS
|
|
1483
1554
|
end
|
1484
1555
|
end
|
1485
1556
|
|
1557
|
+
if RUBY_VERSION >= "1.9.3"
|
1558
|
+
def test_continue
|
1559
|
+
@client.debug_dev = str = ''
|
1560
|
+
res = @client.get(serverurl + 'continue', :header => {:Expect => '100-continue'})
|
1561
|
+
assert_equal(200, res.status)
|
1562
|
+
assert_equal('done!', res.body)
|
1563
|
+
assert_match(/Expect: 100-continue/, str)
|
1564
|
+
end
|
1565
|
+
end
|
1566
|
+
|
1486
1567
|
private
|
1487
1568
|
|
1488
1569
|
def check_query_get(query)
|
@@ -1506,7 +1587,11 @@ private
|
|
1506
1587
|
:DocumentRoot => File.dirname(File.expand_path(__FILE__))
|
1507
1588
|
)
|
1508
1589
|
@serverport = @server.config[:Port]
|
1509
|
-
[
|
1590
|
+
[
|
1591
|
+
:hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3,
|
1592
|
+
:redirect_self, :relative_redirect, :redirect_see_other, :chunked,
|
1593
|
+
:largebody, :status, :compressed, :charset, :continue
|
1594
|
+
].each do |sym|
|
1510
1595
|
@server.mount(
|
1511
1596
|
"/#{sym}",
|
1512
1597
|
WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc)
|
@@ -1560,6 +1645,14 @@ private
|
|
1560
1645
|
res.set_redirect(WEBrick::HTTPStatus::Found, "hello")
|
1561
1646
|
end
|
1562
1647
|
|
1648
|
+
def do_redirect_see_other(req, res)
|
1649
|
+
if req.request_method == 'POST'
|
1650
|
+
res.set_redirect(WEBrick::HTTPStatus::SeeOther, serverurl + "redirect_see_other") # self
|
1651
|
+
else
|
1652
|
+
res.body = 'hello'
|
1653
|
+
end
|
1654
|
+
end
|
1655
|
+
|
1563
1656
|
def do_chunked(req, res)
|
1564
1657
|
res.chunked = true
|
1565
1658
|
piper, pipew = IO.pipe
|
@@ -1597,6 +1690,11 @@ private
|
|
1597
1690
|
res.status = req.query['status'].to_i
|
1598
1691
|
end
|
1599
1692
|
|
1693
|
+
def do_continue(req, res)
|
1694
|
+
req.continue
|
1695
|
+
res.body = 'done!'
|
1696
|
+
end
|
1697
|
+
|
1600
1698
|
class TestServlet < WEBrick::HTTPServlet::AbstractServlet
|
1601
1699
|
def get_instance(*arg)
|
1602
1700
|
self
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpclient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,14 +9,16 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-10 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email: nahi@ruby-lang.org
|
16
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- httpclient
|
17
18
|
extensions: []
|
18
19
|
extra_rdoc_files: []
|
19
20
|
files:
|
21
|
+
- bin/httpclient
|
20
22
|
- lib/httpclient/auth.rb
|
21
23
|
- lib/httpclient/ssl_config.rb
|
22
24
|
- lib/httpclient/http.rb
|
@@ -88,7 +90,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
90
|
version: '0'
|
89
91
|
segments:
|
90
92
|
- 0
|
91
|
-
hash: -
|
93
|
+
hash: -478554732977301043
|
92
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
95
|
none: false
|
94
96
|
requirements:
|
@@ -97,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
99
|
version: '0'
|
98
100
|
segments:
|
99
101
|
- 0
|
100
|
-
hash: -
|
102
|
+
hash: -478554732977301043
|
101
103
|
requirements: []
|
102
104
|
rubyforge_project:
|
103
105
|
rubygems_version: 1.8.23
|