1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* lib/net/http.rb: HTTPHeader holds its header fields as an array (backport from CVS HEAD rev 1.112-1.123). [ruby-list:40629]

* test/net/http/test_httpheader.rb: new file.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@8089 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
aamine 2005-03-06 07:42:35 +00:00
parent 81e29c1cd9
commit 5e4004fa1d
3 changed files with 208 additions and 64 deletions

View file

@ -1,3 +1,10 @@
Sun Mar 6 16:41:33 2005 Minero Aoki <aamine@loveruby.net>
* lib/net/http.rb: HTTPHeader holds its header fields as an array
(backport from CVS HEAD rev 1.112-1.123). [ruby-list:40629]
* test/net/http/test_httpheader.rb: new file.
Sun Mar 6 11:47:10 2005 Sam Roberts <sroberts@uniserve.com>
* lib/pp.rb: rdoced. [ruby-core:4490]

View file

@ -1,8 +1,8 @@
#
# = net/http.rb
#
# Copyright (c) 1999-2003 Yukihiro Matsumoto
# Copyright (c) 1999-2003 Minero Aoki
# Copyright (c) 1999-2005 Yukihiro Matsumoto
# Copyright (c) 1999-2005 Minero Aoki
#
# Written & maintained by Minero Aoki <aamine@loveruby.net>.
#
@ -26,7 +26,6 @@
require 'net/protocol'
require 'uri'
module Net # :nodoc:
# :stopdoc:
@ -48,9 +47,9 @@ module Net # :nodoc:
# (formal version)
#
# require 'net/http'
# Net::HTTP.start('www.example.com', 80) { |http|
# response = http.get('/index.html')
# puts response.body
# Net::HTTP.start('www.example.com', 80) {|http|
# response = http.get('/index.html')
# puts response.body
# }
#
# (shorter version)
@ -67,8 +66,8 @@ module Net # :nodoc:
# === Posting Form Data
#
# require 'net/http'
# Net::HTTP.start('some.www.server', 80) { |http|
# response = http.post('/cgi-bin/search.rb', 'query=ruby')
# Net::HTTP.start('some.www.server', 80) {|http|
# response = http.post('/cgi-bin/search.rb', 'query=ruby')
# }
#
# === Accessing via Proxy
@ -83,7 +82,7 @@ module Net # :nodoc:
# proxy_port = 8080
# :
# Net::HTTP::Proxy(proxy_addr, proxy_port).start('www.example.com') {|http|
# # always connect to your.proxy.addr:8080
# # always connect to your.proxy.addr:8080
# :
# }
#
@ -118,7 +117,7 @@ module Net # :nodoc:
# require 'net/http'
# require 'uri'
#
# def fetch( uri_str, limit = 10 )
# def fetch(uri_str, limit = 10)
# # You should choose better exception.
# raise ArgumentError, 'HTTP redirect too deep' if limit == 0
#
@ -160,13 +159,13 @@ module Net # :nodoc:
# allows you to use 1.2 features again.
#
# # example
# Net::HTTP.start { |http1| ...(http1 has 1.2 features)... }
# Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
#
# Net::HTTP.version_1_1
# Net::HTTP.start { |http2| ...(http2 has 1.1 features)... }
# Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
#
# Net::HTTP.version_1_2
# Net::HTTP.start { |http3| ...(http3 has 1.2 features)... }
# Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
#
# This function is NOT thread-safe.
#
@ -831,7 +830,7 @@ module Net # :nodoc:
req.exec @socket, @curr_http_version, edit_path(req.path), body
begin
res = HTTPResponse.read_new(@socket)
end while HTTPContinue === res
end while res.kind_of?(HTTPContinue)
res.reading_body(@socket, req.response_body_permitted?) {
yield res if block_given?
}
@ -913,6 +912,17 @@ module Net # :nodoc:
#
module HTTPHeader
def initialize_http_header(h)
@header = {}
return unless h
h.each do |k,v|
key = k.downcase
$stderr.puts "net/http: warning: duplicated HTTP header: #{k}" if @header.key?(key) and $VERBOSE
@header[key] = [v.strip]
end
end
private :initialize_http_header
def size #:nodoc: obsolete
@header.size
end
@ -920,26 +930,83 @@ module Net # :nodoc:
alias length size #:nodoc: obsolete
# Returns the header field corresponding to the case-insensitive key.
# For example, a key of "Content-Type" might return "text/html"
# See also #get_fields.
#
# p response['Content-Type'] #=> "text/html; charset=utf-8"
# p response['cOnTeNt-tYpE'] #=> "text/html; charset=utf-8"
#
def [](key)
@header[key.downcase]
a = @header[key.downcase] or return nil
a.join(', ')
end
# [Ruby 1.8.3]
# Returns an array of header field strings corresponding to the
# case-insensitive +key+. This method allows you to get duplicated
# header fields without any processing. See also #[].
#
# p response.get_fields('Set-Cookie')
# #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23",
# "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"]
# p response['Set-Cookie']
# #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"
#
def get_fields(key)
return nil unless @header[key.downcase]
@header[key.downcase].dup
end
# Sets the header field corresponding to the case-insensitive key.
# See also #add_field.
#
# request['My-Header'] = 'a'
# p request['My-Header'] #=> "a"
# request['My-Header'] = 'b'
# p request['My-Header'] #=> "b"
#
def []=(key, val)
@header[key.downcase] = val
unless val
@header.delete key.downcase
return val
end
@header[key.downcase] = [val].flatten.map {|s| s.to_str }
end
# [Ruby 1.8.3]
# Adds header field instead of replace.
# Second argument +val+ must be a String.
# See also #[]=, #[] and #get_fields.
#
# request.add_field 'X-My-Header', 'a'
# p request['X-My-Header'] #=> "a"
# p request.get_fields('X-My-Header') #=> ["a"]
# request.add_field 'X-My-Header', 'b'
# p request['X-My-Header'] #=> "a, b"
# p request.get_fields('X-My-Header') #=> ["a", "b"]
# request.add_field 'X-My-Header', 'c'
# p request['X-My-Header'] #=> "a, b, c"
# p request.get_fields('X-My-Header') #=> ["a", "b", "c"]
#
def add_field(key, val)
if @header[key.downcase]
@header[key.downcase].push val
else
@header[key.downcase] = [val]
end
end
# Returns the header field corresponding to the case-insensitive key.
# Returns the default value +args+, or the result of the block, or nil,
# if there's no header field named key. See Hash#fetch
def fetch(key, *args, &block) #:yield: +key+
@header.fetch(key.downcase, *args, &block)
@header.fetch(key.downcase, *args, &block).join(', ')
end
# Iterates for each header names and values.
def each_header(&block) #:yield: +key+, +value+
@header.each(&block)
@header.each do |k, va|
yield k, va.join(', ')
end
end
alias each each_header
@ -951,7 +1018,9 @@ module Net # :nodoc:
# Iterates for each header values.
def each_value(&block) #:yield: +value+
@header.each_value(&block)
@header.each_value do |va|
yield va.join(', ')
end
end
# Removes a header field.
@ -966,17 +1035,21 @@ module Net # :nodoc:
# Returns a Hash consist of header names and values.
def to_hash
@header.dup
h = {}
@header.each do |k, va|
h[k] = va.join(', ')
end
h
end
# As for #each_header, except the keys are provided in capitalized form.
def canonical_each
@header.each do |k,v|
yield canonical(k), v
@header.each do |k, va|
yield canonical(k), va.join(', ')
end
end
def canonical( k )
def canonical(k)
k.split(/-/).map {|i| i.capitalize }.join('-')
end
private :canonical
@ -984,8 +1057,8 @@ module Net # :nodoc:
# Returns a Range object which represents Range: header field,
# or +nil+ if there is no such header.
def range
s = @header['range'] or return nil
s.split(/,/).map {|spec|
return nil unless @header['range']
self['Range'].split(/,/).map {|spec|
m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match(spec) or
raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
d1 = m[1].to_i
@ -1025,7 +1098,7 @@ module Net # :nodoc:
raise TypeError, 'Range/Integer is required'
end
@header['range'] = "bytes=#{s}"
@header['range'] = ["bytes=#{s}"]
r
end
@ -1034,10 +1107,10 @@ module Net # :nodoc:
# Returns an Integer object which represents the Content-Length: header field
# or +nil+ if that field is not provided.
def content_length
s = @header['content-length'] or return nil
m = /\d+/.match(s) or
return nil unless @header['content-length']
len = self['Content-Length'].slice(/\d+/) or
raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
m[0].to_i
len.to_i
end
# Returns "true" if the "transfer-encoding" header is present and
@ -1045,34 +1118,35 @@ module Net # :nodoc:
# the content to be sent in "chunks" without at the outset
# stating the entire content length.
def chunked?
s = @header['transfer-encoding']
(s and /(?:\A|[^\-\w])chunked(?:[^\-\w]|\z)/i === s) ? true : false
return false unless @header.key?('transfer-encoding')
s = self['Transfer-Encoding']
/(?:\A|[^\-\w])chunked(?:[^\-\w]|\z)/i =~ s ? true : false
end
# Returns a Range object which represents Content-Range: header field.
# This indicates, for a partial entity body, where this fragment
# fits inside the full entity body, as range of byte offsets.
def content_range
s = @header['content-range'] or return nil
m = %r<bytes\s+(\d+)-(\d+)/(?:\d+|\*)>i.match(s) or
return nil unless @header['content-range']
m = %r<bytes\s+(\d+)-(\d+)/(?:\d+|\*)>i.match(self['Content-Range']) or
raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
m[1].to_i .. m[2].to_i + 1
end
# The length of the range represented in Range: header.
def range_length
r = self.content_range
r and (r.end - r.begin)
r = content_range() or return nil
r.end - r.begin
end
# Set the Authorization: header for "Basic" authorization.
def basic_auth(account, password)
@header['authorization'] = basic_encode(account, password)
@header['authorization'] = [basic_encode(account, password)]
end
# Set Proxy-Authorization: header for "Basic" authorization.
def proxy_basic_auth(account, password)
@header['proxy-authorization'] = basic_encode(account, password)
@header['proxy-authorization'] = [basic_encode(account, password)]
end
def basic_encode(account, password)
@ -1097,15 +1171,8 @@ module Net # :nodoc:
@request_has_body = reqbody
@response_has_body = resbody
@path = path
@header = {}
return unless initheader
initheader.each do |k,v|
key = k.downcase
$stderr.puts "net/http: warning: duplicated HTTP header: #{k}" if @header.key?(key) and $VERBOSE
@header[key] = v.strip
end
@header['accept'] ||= '*/*'
initialize_http_header initheader
self['Accept'] ||= '*/*'
end
attr_reader :method
@ -1145,15 +1212,13 @@ module Net # :nodoc:
raise ArgumentError, 'HTTP request body is not permitted'
end
def send_request_with_body( sock, ver, path, body )
@header['content-length'] = body.length.to_s
@header.delete 'transfer-encoding'
unless @header['content-type']
def send_request_with_body(sock, ver, path, body)
self['Content-Length'] = body.length.to_s
self.delete 'Transfer-Encoding'
unless self['Content-Type']
$stderr.puts 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE
@header['content-type'] = 'application/x-www-form-urlencoded'
self['Content-Type'] = 'application/x-www-form-urlencoded'
end
request sock, ver, path
sock.write body
end
@ -1538,13 +1603,8 @@ module Net # :nodoc:
httpv, code, msg = read_status_line(sock)
res = response_class(code).new(httpv, code, msg)
each_response_header(sock) do |k,v|
if res.key?(k)
res[k] << ', ' << v
else
res[k] = v
end
res.add_field k, v
end
res
end
@ -1585,8 +1645,7 @@ module Net # :nodoc:
@http_version = httpv
@code = code
@message = msg
@header = {}
initialize_http_header nil
@body = nil
@read = false
end

View file

@ -6,7 +6,7 @@ class HTTPHeaderTest < Test::Unit::TestCase
class C
include Net::HTTPHeader
def initialize
@header = {}
initialize_http_header({})
end
end
@ -49,25 +49,91 @@ class HTTPHeaderTest < Test::Unit::TestCase
@c['Next-Header'] = 'next string'
assert_equal 'next string', @c['next-header']
end
def test_add_field
@c.add_field 'My-Header', 'a'
assert_equal 'a', @c['My-Header']
assert_equal ['a'], @c.get_fields('My-Header')
@c.add_field 'My-Header', 'b'
assert_equal 'a, b', @c['My-Header']
assert_equal ['a', 'b'], @c.get_fields('My-Header')
@c.add_field 'My-Header', 'c'
assert_equal 'a, b, c', @c['My-Header']
assert_equal ['a', 'b', 'c'], @c.get_fields('My-Header')
@c.add_field 'My-Header', 'd, d'
assert_equal 'a, b, c, d, d', @c['My-Header']
assert_equal ['a', 'b', 'c', 'd, d'], @c.get_fields('My-Header')
end
def test_get_fields
@c['My-Header'] = 'test string'
assert_equal ['test string'], @c.get_fields('my-header')
assert_equal ['test string'], @c.get_fields('My-header')
assert_equal ['test string'], @c.get_fields('my-Header')
assert_nil @c.get_fields('not-found')
assert_nil @c.get_fields('Not-Found')
@c.get_fields('my-header').push 'junk'
assert_equal ['test string'], @c.get_fields('my-header')
@c.get_fields('my-header').clear
assert_equal ['test string'], @c.get_fields('my-header')
end
def test_delete
@c['My-Header'] = 'test'
assert_equal 'test', @c['My-Header']
assert_nil @c['not-found']
@c.delete 'My-Header'
assert_nil @c['My-Header']
assert_nil @c['not-found']
@c.delete 'My-Header'
@c.delete 'My-Header'
assert_nil @c['My-Header']
assert_nil @c['not-found']
end
def test_each
@c['My-Header'] = 'test'
@c.each do |k, v|
assert_equal 'my-header', k
assert_equal 'test', v
end
@c.each do |k, v|
assert_equal 'my-header', k
assert_equal 'test', v
end
end
def test_each_key
@c['My-Header'] = 'test'
@c.each_key do |k|
assert_equal 'my-header', k
end
@c.each_key do |k|
assert_equal 'my-header', k
end
end
def test_each_value
@c['My-Header'] = 'test'
@c.each_value do |v|
assert_equal 'test', v
end
@c.each_value do |v|
assert_equal 'test', v
end
end
def test_canonical_each
@c['my-header'] = ['a', 'b']
@c.canonical_each do |k,v|
assert_equal 'My-Header', k
assert_equal 'a, b', v
end
end
=begin
def test_each_capitalized
@c['my-header'] = ['a', 'b']
@c.each_capitalized do |k,v|
@ -75,8 +141,16 @@ class HTTPHeaderTest < Test::Unit::TestCase
assert_equal 'a, b', v
end
end
=end
def test_key?
@c['My-Header'] = 'test'
assert_equal true, @c.key?('My-Header')
assert_equal true, @c.key?('my-header')
assert_equal false, @c.key?('Not-Found')
assert_equal false, @c.key?('not-found')
assert_equal false, @c.key?('')
assert_equal false, @c.key?('x' * 1024)
end
def test_to_hash
@ -153,6 +227,7 @@ class HTTPHeaderTest < Test::Unit::TestCase
assert_equal len, @c.content_length
end
=begin
def test_content_length=
@c.content_length = 0
assert_equal 0, @c.content_length
@ -163,7 +238,9 @@ class HTTPHeaderTest < Test::Unit::TestCase
@c.content_length = 10000000000000
assert_equal 10000000000000, @c.content_length
end
=end
=begin
def test_content_type
@c.content_type = 'text/html'
assert_equal 'text/html', @c.content_type
@ -202,6 +279,7 @@ class HTTPHeaderTest < Test::Unit::TestCase
def test_set_content_type
end
=end
def test_basic_auth
end