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 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