diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb
index b0ec4b06..a81196b9 100644
--- a/lib/net/http/header.rb
+++ b/lib/net/http/header.rb
@@ -1,16 +1,154 @@
# frozen_string_literal: false
-# The HTTPHeader module defines methods for reading and writing
-# HTTP headers.
#
-# It is used as a mixin by other classes, to provide hash-like
-# access to HTTP header values. Unlike raw hash access, HTTPHeader
-# provides access via case-insensitive keys. It also provides
-# methods for accessing commonly-used HTTP header values in more
-# convenient formats.
+# The \HTTPHeader module provides access to \HTTP headers.
+# The headers are a hash-like collection of key/value pairs called _fields_.
+#
+# The module is included in:
+#
+# - Net::HTTPGenericRequest (and therefore Net::HTTPRequest).
+# - Net::HTTPResponse.
+#
+# == About the Examples
+#
+# :include: doc/net-http/examples.rdoc
+#
+# == Fields
+#
+# A header field is a key/value pair.
+#
+# === Field Keys
+#
+# A field key may be:
+#
+# - A string: Key 'Accept' is treated as if it were
+# 'Accept'.downcase; i.e., 'accept'.
+# - A symbol: Key :Accept is treated as if it were
+# :Accept.to_s.downcase; i.e., 'accept'.
+#
+# Examples:
+#
+# req = Net::HTTP::Get.new(uri)
+# req[:accept] # => "*/*"
+# req['Accept'] # => "*/*"
+# req['ACCEPT'] # => "*/*"
+#
+# req['accept'] = 'text/html'
+# req[:accept] = 'text/html'
+# req['ACCEPT'] = 'text/html'
+#
+# === Field Values
+#
+# A field value may be returned as an array of strings or as a string:
+#
+# - These methods return field values as arrays:
+#
+# - #get_fields: Returns the array value for the given key,
+# or +nil+ if it does not exist.
+# - #to_hash: Returns a hash of all header fields:
+# each key is a field name; its value is the array value for the field.
+#
+# - These methods return field values as string;
+# the string value for a field is equivalent to
+# self[key.downcase.to_s].join(', ')):
+#
+# - #[]: Returns the string value for the given key,
+# or +nil+ if it does not exist.
+# - #fetch: Like #[], but accepts a default value
+# to be returned if the key does not exist.
+#
+# The field value may be set:
+#
+# - #[]=: Sets the value for the given key;
+# the given value may be a string, a symbol, an array, or a hash.
+# - #add_field: Adds a given value to a value for the given key
+# (not overwriting the existing value).
+# - #delete: Deletes the field for the given key.
+#
+# Example field values:
+#
+# - \String:
+#
+# req['Accept'] = 'text/html' # => "text/html"
+# req['Accept'] # => "text/html"
+# req.get_fields('Accept') # => ["text/html"]
+#
+# - \Symbol:
+#
+# req['Accept'] = :text # => :text
+# req['Accept'] # => "text"
+# req.get_fields('Accept') # => ["text"]
+#
+# - Simple array:
+#
+# req[:foo] = %w[bar baz bat]
+# req[:foo] # => "bar, baz, bat"
+# req.get_fields(:foo) # => ["bar", "baz", "bat"]
+#
+# - Simple hash:
+#
+# req[:foo] = {bar: 0, baz: 1, bat: 2}
+# req[:foo] # => "bar, 0, baz, 1, bat, 2"
+# req.get_fields(:foo) # => ["bar", "0", "baz", "1", "bat", "2"]
+#
+# - Nested:
+#
+# req[:foo] = [%w[bar baz], {bat: 0, bam: 1}]
+# req[:foo] # => "bar, baz, bat, 0, bam, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "0", "bam", "1"]
+#
+# req[:foo] = {bar: %w[baz bat], bam: {bah: 0, bad: 1}}
+# req[:foo] # => "bar, baz, bat, bam, bah, 0, bad, 1"
+# req.get_fields(:foo) # => ["bar", "baz", "bat", "bam", "bah", "0", "bad", "1"]
+#
+# == Convenience Methods
+#
+# Various convenience methods retrieve values, set values, query values,
+# set form values, or iterate over fields.
+#
+# === Getters
+#
+# - #[]: Returns the string value for the given field.
+# - #content_length: Returns the integer value of field 'Content-Length'.
+# - #content_range: Returns the Range value of field 'Content-Range'.
+# - #content_type: Returns the string value of field 'Content-Type'.
+# - #main_type: Returns first part of the string value of field 'Content-Type'.
+# - #sub_type: Returns second part of the string value of field 'Content-Type'.
+# - #range: Returns an array of Range objects of field 'Range', or +nil+.
+# - #range_length: Returns the integer length of the range given in field 'Content-Range'.
+# - #type_params: Returns the string parameters for 'Content-Type'.
+#
+# === Setters
+#
+# - #[]=: Sets the string or array value for the given field.
+# - #basic_auth: Sets the string authorization header for 'Authorization'.
+# - #content_length=: Sets the integer length for field 'Content-Length.
+# - #content_type=: Sets the string value for field 'Content-Type'.
+# - #proxy_basic_auth: Sets the string authorization header for 'Proxy-Authorization'.
+# - #range=: Sets the value for field +'Range'+.
+#
+# === Queries
+#
+# - #chunked?: Returns whether field 'Transfer-Encoding' is set to 'chunked'.
+# - #connection_close?: Returns whether field 'Connection' is set to 'close'.
+# - #connection_keep_alive?: Returns whether field 'Connection' is set to 'keep-alive'.
+# - #key?: Returns whether a given field exists.
+#
+# === Form Setters
+#
+# - #set_form: Sets an HTML form data set.
+# - #set_form_data: Sets header fields and a body from HTML form data.
+#
+# === Iterators
+#
+# - #each_capitalized: Passes each field capitalized-name/value pair to the block.
+# - #each_capitalized_name: Passes each capitalized field name to the block.
+# - #each_header: Passes each field name/value pair to the block.
+# - #each_name: Passes each field name to the block.
+# - #each_value: Passes each field value to the block.
#
module Net::HTTPHeader
- def initialize_http_header(initheader)
+ def initialize_http_header(initheader) #:nodoc:
@header = {}
return unless initheader
initheader.each do |key, value|
@@ -33,14 +171,30 @@ def size #:nodoc: obsolete
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"
+ # Returns the string field value for the case-insensitive field +key+,
+ # or +nil+ if there is no such key;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Accept'] # => "*/*"
+ # req['Foo'] = %w[bar baz bat]
+ # req['Foo'] # => "bar, baz, bat"
+ # res['Nosuch'] # => nil
+ #
def [](key)
a = @header[key.downcase.to_s] or return nil
a.join(', ')
end
- # Sets the header field corresponding to the case-insensitive key.
+ # Sets the value for the case-insensitive +key+ to +val+,
+ # overwriting the previous value if the field exists;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Accept'] # => "*/*"
+ # req['Accept'] = 'text/html'
+ # req['Accept'] # => "text/html"
+ #
def []=(key, val)
unless val
@header.delete key.downcase.to_s
@@ -49,20 +203,18 @@ def []=(key, val)
set_field(key, val)
end
- # [Ruby 1.8.3]
- # Adds a value to a named header field, instead of replacing its value.
- # Second argument +val+ must be a String.
- # See also #[]=, #[] and #get_fields.
+ # Adds value +val+ to the value array for field +key+ if the field exists;
+ # creates the field with the given +key+ and +val+ if it does not exist.
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@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"]
+ # req = Net::HTTP::Get.new(uri)
+ # req.add_field('Foo', 'bar')
+ # req['Foo'] # => "bar"
+ # req.add_field('Foo', 'baz')
+ # req['Foo'] # => "bar, baz"
+ # req.add_field('Foo', %w[baz bam])
+ # req['Foo'] # => "bar, baz, baz, bam"
+ # req.get_fields('Foo') # => ["bar", "baz", "baz", "bam"]
#
def add_field(key, val)
stringified_downcased_key = key.downcase.to_s
@@ -101,16 +253,16 @@ def add_field(key, val)
end
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 #[].
+ # Returns the array field value for the given +key+,
+ # or +nil+ if there is no such field;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
#
- # 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"
+ # req = Net::HTTP::Get.new(uri)
+ # req['Foo'] = 'bar'
+ # req.get_fields('Foo') # => ["bar"]
+ # req.add_field('Foo', 'baz')
+ # req.get_fields('Foo') # => ["bar", "baz"]
+ # req.get_fields('Nosuch') # => nil
#
def get_fields(key)
stringified_downcased_key = key.downcase.to_s
@@ -118,24 +270,48 @@ def get_fields(key)
@header[stringified_downcased_key].dup
end
- # Returns the header field corresponding to the case-insensitive key.
- # Returns the default value +args+, or the result of the block, or
- # raises an IndexError if there's no header field named +key+
- # See Hash#fetch
+ # :call-seq
+ # fetch(key, default_val = nil) {|key| ... } -> object
+ # fetch(key, default_val = nil) -> value or default_val
+ #
+ # With a block, returns the string value for +key+ if it exists;
+ # otherwise returns the value of the block;
+ # ignores the +default_val+;
+ # see {Fields}[rdoc-ref:Net::HTTPHeader@Fields]:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req['Foo'] = 'bar'
+ # req.fetch('Foo') {|key| key.capitalize } # => "bar"
+ # req.fetch('Nosuch') {|key| key.capitalize } # => "Nosuch"
+ #
+ # With no block, returns the string value for +key+ if it exists;
+ # otherwise, returns +default_val+ if it was given;
+ # otherwise raises an exception:
+ #
+ # req.fetch('Foo') # => "bar"
+ # req.fetch('Nosuch', :baz) # => :baz
+ # req.fetch('Nosuch') # Raises KeyError.
+ #
def fetch(key, *args, &block) #:yield: +key+
a = @header.fetch(key.downcase.to_s, *args, &block)
a.kind_of?(Array) ? a.join(', ') : a
end
- # Iterates through the header names and values, passing in the name
- # and value to the code block supplied.
+ # Calls the block with each key/value pair:
#
- # Returns an enumerator if no block is given.
+ # req = Net::HTTP::Get.new(uri)
+ # req.each_header {|key, value| p [key, value] }
#
- # Example:
+ # Output:
#
- # response.header.each_header {|key,value| puts "#{key} = #{value}" }
+ # ["accept-encoding", "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"]
+ # ["accept", "*/*"]
+ # ["user-agent", "Ruby"]
+ # ["host", "jsonplaceholder.typicode.com"]
+ #
+ # Returns an enumerator if no block is given.
#
+ # Net::HTTPHeader#each is an alias for Net::HTTPHeader#each_header.
def each_header #:yield: +key+, +value+
block_given? or return enum_for(__method__) { @header.size }
@header.each do |k,va|
@@ -145,10 +321,21 @@ def each_header #:yield: +key+, +value+
alias each each_header
- # Iterates through the header names in the header, passing
- # each header name to the code block.
+ # Calls the block with each field key:
+ #
+ # req = Net::HTTP::Get.new(uri)
+ # req.each_key {|key| p key }
+ #
+ # Output:
+ #
+ # "accept-encoding"
+ # "accept"
+ # "user-agent"
+ # "host"
#
# Returns an enumerator if no block is given.
+ #
+ # Net::HTTPHeader#each_name is an alias for Net::HTTPHeader#each_key.
def each_name(&block) #:yield: +key+
block_given? or return enum_for(__method__) { @header.size }
@header.each_key(&block)
@@ -156,12 +343,20 @@ def each_name(&block) #:yield: +key+
alias each_key each_name
- # Iterates through the header names in the header, passing
- # capitalized header names to the code block.
+ # Calls the block with each capitalized field name:
#
- # Note that header names are capitalized systematically;
- # capitalization may not match that used by the remote HTTP
- # server in its response.
+ # req = Net::HTTP::Get.new(uri)
+ # req.each_capitalized_name {|key| p key }
+ #
+ # Output:
+ #
+ # "Accept-Encoding"
+ # "Accept"
+ # "User-Agent"
+ # "Host"
+ #
+ # The capitalization is system-dependent;
+ # see {Case Mapping}[https://docs.ruby-lang.org/en/master/case_mapping_rdoc.html].
#
# Returns an enumerator if no block is given.
def each_capitalized_name #:yield: +key+
@@ -208,6 +403,8 @@ def to_hash
# server in its response.
#
# Returns an enumerator if no block is given.
+ #
+ # Net::HTTPHeader#canonical_each is an alias for Net::HTTPHeader#each_capitalized.
def each_capitalized
block_given? or return enum_for(__method__) { @header.size }
@header.each do |k,v|
@@ -394,7 +591,9 @@ def type_params
# Sets the content type in an HTTP header.
# The +type+ should be a full HTTP content type, e.g. "text/html".
# The +params+ are an optional Hash of parameters to add after the
- # content type, e.g. {'charset' => 'iso-8859-1'}
+ # content type, e.g. {'charset' => 'iso-8859-1'}.
+ #
+ # Net::HTTPHeader#content_type= is an alias for Net::HTTPHeader#set_content_type.
def set_content_type(type, params = {})
@header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
end
@@ -414,6 +613,7 @@ def set_content_type(type, params = {})
# http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
# http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')
#
+ # Net::HTTPHeader#form_data= is an alias for Net::HTTPHeader#set_form_data.
def set_form_data(params, sep = '&')
query = URI.encode_www_form(params)
query.gsub!(/&/, sep) if sep != '&'