httpclient 2.2.7 → 2.3.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/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
|