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 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
- Some part of code in http-access.rb was recycled in http-access2.rb.
48
- Those part is copyrighted by Maehashi-san who made and distributed
49
- http-access/0.0.4. Many thanks to Maehashi-san.
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
- Thanks Andrew France for the report!
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
- responses like 304 Not Modified. Thanks to Florian Hars.
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
- request that has urlencoded entity-body.
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
- adding a thread-safe lazily initialized class-level HTTPClient object
123
- to your class.
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
- e.g. `{ x: 'a', y: [1,2,3] }` is encoded into `"x=a&y=1&y=2&y=3"`.
129
- Thanks to Akinori MUSHA.
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
- as part of response Message::Header.
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
- On Windows, calling File#size fails with an Unknown error (20047).
147
- This workaround uses File#lstat instead.
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
- It doesn't override given Accept header from API.
199
+ It doesn't override given Accept header from API.
157
200
  * Add HTTPClient::SSLConfig#set_default_paths. This method makes
158
- HTTPClient instance to use OpenSSL's default trusted CA certificates.
201
+ HTTPClient instance to use OpenSSL's default trusted CA certificates.
159
202
  * Allow to set Date header manually.
160
- ex. clent.get(uri, :header => {'Date' => Time.now.httpdate})
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
- ("192.168.1.1, 192.168.1.2")
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.
@@ -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
@@ -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 part
9
- # of code in http-access.rb was recycled in http-access2.rb. Those part is
10
- # copyrighted by Maehashi-san.
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'
@@ -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 = URI.parse(res.header['location'][0])
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
- unless newuri.is_a?(URI::HTTP)
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 = URI.parse(res.header['location'][0])
644
- unless newuri.is_a?(URI::HTTP)
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 HTTP::Status.successful?(r.status)
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 HTTP::Status.redirect?(res.status)
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 HTTP::Status.successful?(res.status)
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
- unless @no_proxy
1045
- return false
1046
- end
1047
- @no_proxy.scan(/([^:,]+)(?::(\d+))?/) do |host, port|
1048
- if /(\A|\.)#{Regexp.quote(host)}\z/i =~ uri.host &&
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
@@ -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 = URI.parse(col[0])
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'
@@ -188,7 +188,7 @@ module HTTP
188
188
  end
189
189
 
190
190
  # Placeholder URI object for nil uri.
191
- NIL_URI = URI.parse('http://nil-uri-given/')
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.respond_to?(:size) ? body.size - body.pos : nil
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.key?(io)
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
- if part.respond_to?(:lstat)
604
- @size += part.lstat.size
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
@@ -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
- # Some part of code in http-access.rb was recycled in httpclient.rb.
10
- # Those part is copyrighted by Maehashi-san.
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, URI.parse(@dest.to_s)) if @proxy
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, URI.parse(@dest.to_s)) if @proxy
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
- initial_line = @socket.gets("\n")
876
- if initial_line.nil?
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
- # TODO: change to "SSLv3" in future versions to make harder to use SSLv2.
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
@@ -1,14 +1,11 @@
1
1
  # HTTPClient - HTTP client library.
2
- # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
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
 
@@ -1,3 +1,3 @@
1
1
  class HTTPClient
2
- VERSION = '2.2.7'
2
+ VERSION = '2.3.0'
3
3
  end
@@ -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 = URI.parse('http://www.rubycolor.org/hoge/funi/#919191')
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 = URI.parse('https://www.rubycolor.org/hoge/funi/#919191')
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,URI.parse('http://www.test.jp'))
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,URI.parse('http://www.excite.co.jp'))
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,URI.parse('http://www.excite.co.jp'))
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,URI.parse('http://www.excite.co.jp'))
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,URI.parse('http://www.test.jp'))
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,URI.parse('http://www.test.jp'))
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,URI.parse('http://www.test.jp'))
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, URI.parse("http://www.excite2.co.jp/"))
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, URI.parse("http://www.excite.co.jp/"))
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 = URI.parse('http://www.excite.co.jp/hoge/funi/')
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 = URI.parse('http://www.example.org')
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 = URI.parse("http://www.inac.co.jp/hoge")
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 = URI.parse("http://www.inac.co.jp/hoge/hoge2/hoge3")
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 = URI.parse("http://www.inac.co.jp/hoge/hoge2/hoge3")
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
@@ -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(URI.parse(proxyurl), @client.proxy)
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
- assert_raises(URI::InvalidURIError) do
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(URI.parse("http://foo:1234"), @client.proxy)
124
- uri = URI.parse("http://bar:2345")
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
  #
@@ -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(URI.parse(proxyurl), @client.proxy)
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
- assert_raises(URI::InvalidURIError) do
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(URI.parse("http://admin:admin@foo:1234"), @client.proxy)
184
- uri = URI.parse("http://bar:2345")
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(URI.parse("http://admin:admin@foo:1234"), client.proxy)
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(URI.parse("http://admin:admin@foo:1234"), client.proxy)
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(URI('http://google.com/'), @client.get('http://google.com/').header.request_uri)
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 = URI(serverurl + 'hello')
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 = URI.parse(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 = URI.parse(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 = URI(serverurl + 'hello')
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(3, myio.called)
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(URI.parse(uri), urify(uri))
1130
- assert_equal(URI.parse(uri), urify(URI.parse(uri)))
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(URI.parse('http://localhost:12345/foo'))
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(URI.parse('http://localhost:12341/'))
1161
- site2 = HTTPClient::Site.new(URI.parse('http://localhost:12342/'))
1162
- site3 = HTTPClient::Site.new(URI.parse('http://localhost:12342/'))
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(URI.parse('https://foo/bar'))
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(URI.parse('https://example.com/'))
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
- [:hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect, :chunked, :largebody, :status, :compressed, :charset].each do |sym|
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.2.7
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-08-14 00:00:00.000000000 Z
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: -2044284490282545774
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: -2044284490282545774
102
+ hash: -478554732977301043
101
103
  requirements: []
102
104
  rubyforge_project:
103
105
  rubygems_version: 1.8.23