module Gem::Net::HTTPHeader
def [](key)
Note that some field values may be retrieved via convenience methods;
res['Nosuch'] # => nil
res['Connection'] # => "keep-alive"
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
or +nil+ if there is no such key;
Returns the string field value for the case-insensitive field +key+,
def [](key) a = @header[key.downcase.to_s] or return nil a.join(', ') end
def []=(key, val)
Note that some field values may be set via convenience methods;
req['Accept'] # => "text/html"
req['Accept'] = 'text/html'
req['Accept'] # => "*/*"
req = Gem::Net::HTTP::Get.new(uri)
see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
overwriting the previous value if the field exists;
Sets the value for the case-insensitive +key+ to +val+,
def []=(key, val) unless val @header.delete key.downcase.to_s return val end set_field(key, val) end
def add_field(key, val)
req.get_fields('Foo') # => ["bar", "baz", "baz", "bam"]
req['Foo'] # => "bar, baz, baz, bam"
req.add_field('Foo', %w[baz bam])
req['Foo'] # => "bar, baz"
req.add_field('Foo', 'baz')
req['Foo'] # => "bar"
req.add_field('Foo', 'bar')
req = Gem::Net::HTTP::Get.new(uri)
see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
creates the field with the given +key+ and +val+ if it does not exist.
Adds value +val+ to the value array for field +key+ if the field exists;
def add_field(key, val) stringified_downcased_key = key.downcase.to_s if @header.key?(stringified_downcased_key) append_field_value(@header[stringified_downcased_key], val) else set_field(key, val) end end
def append_field_value(ary, val)
def append_field_value(ary, val) al numerable each{|x| append_field_value(ary, x)} = val.to_s [\r\n]/n.match?(val.b) ise ArgumentError, 'header field value cannot include CR/LF' push val
def basic_auth(account, password)
# => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
req['Authorization']
req.basic_auth('my_account', 'my_password')
+account+ and +password+ strings:
Sets header 'Authorization' using the given
def basic_auth(account, password) @header['authorization'] = [basic_encode(account, password)] end
def basic_encode(account, password)
def basic_encode(account, password) 'Basic ' + ["#{account}:#{password}"].pack('m0') end
def capitalize(name)
def capitalize(name) name.to_s.split('-'.freeze).map {|s| s.capitalize }.join('-'.freeze) end
def chunked?
res.chunked? # => true
res['Transfer-Encoding'] # => "chunked"
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]:
+false+ otherwise;
exists and has value 'chunked',
Returns +true+ if field 'Transfer-Encoding'
def chunked? return false unless @header['transfer-encoding'] field = self['Transfer-Encoding'] (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false end
def connection_close?
def connection_close? token = /(?:\A|,)\s*close\s*(?:\z|,)/i @header['connection']&.grep(token) {return true} @header['proxy-connection']&.grep(token) {return true} false end
def connection_keep_alive?
def connection_keep_alive? token = /(?:\A|,)\s*keep-alive\s*(?:\z|,)/i @header['connection']&.grep(token) {return true} @header['proxy-connection']&.grep(token) {return true} false end
def content_length
res.content_length # => nil
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
res.content_length # => 2
res = Gem::Net::HTTP.get_response(hostname, '/nosuch/1')
see {Content-Length request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-request-header]:
or +nil+ if there is no such field;
Returns the value of field 'Content-Length' as an integer,
def content_length return nil unless key?('Content-Length') len = self['Content-Length'].slice(/\d+/) or raise Gem::Net::HTTPHeaderSyntaxError, 'wrong Content-Length format' len.to_i end
def content_length=(len)
end # => #
http.request(req)
res = Gem::Net::HTTP.start(hostname) do |http|
req.content_type = 'application/json'
req.content_length = req.body.size # => 42
req.body = '{"title": "foo","body": "bar","userId": 1}'
req = Gem::Net::HTTP::Post.new(_uri) # => #
_uri.path = '/posts' # => "/posts"
hostname = _uri.hostname # => "jsonplaceholder.typicode.com"
_uri = uri.dup
see {Content-Length response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-response-header]:
Sets the value of field 'Content-Length' to the given numeric;
def content_length=(len) unless len @header.delete 'content-length' return nil end @header['content-length'] = [len.to_i.to_s] end
def content_range
res.content_range # => 0..499
res['Content-Range'] # => "bytes 0-499/1000"
res['Content-Range'] = 'bytes 0-499/1000'
res['Content-Range'] # => nil
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
'Content-Range', or +nil+ if no such field exists;
Returns a Range object representing the value of field
def content_range return nil unless @header['content-range'] m = %r<\A\s*(\w+)\s+(\d+)-(\d+)/(\d+|\*)>.match(self['Content-Range']) or raise Gem::Net::HTTPHeaderSyntaxError, 'wrong Content-Range format' return unless m[1] == 'bytes' m[2].to_i .. m[3].to_i end
def content_type
res.content_type # => "application/json"
res['content-type'] # => "application/json; charset=utf-8"
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
or +nil+ if no such field exists;
from the value of field 'Content-Type',
Returns the {media type}[https://en.wikipedia.org/wiki/Media_type]
def content_type main = main_type() return nil unless main sub = sub_type() if sub "#{main}/#{sub}" else main end end
def delete(key)
req.delete('Nosuch') # => nil
req.delete('Accept') # => ["*/*"]
req = Gem::Net::HTTP::Get.new(uri)
returns the deleted value, or +nil+ if no such field exists:
(see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]);
Removes the header for the given case-insensitive +key+
def delete(key) @header.delete(key.downcase.to_s) end
def each_capitalized
Like #each_header, but the keys are returned in capitalized form.
def each_capitalized block_given? or return enum_for(__method__) { @header.size } @header.each do |k,v| yield capitalize(k), v.join(', ') end end
def each_capitalized_name #:yield: +key+
Returns an enumerator if no block is given.
see {Case Mapping}[https://docs.ruby-lang.org/en/master/case_mapping_rdoc.html].
The capitalization is system-dependent;
"Cf-Ray"
"Cf-Cache-Status"
"Cache-Control"
"Connection"
"Content-Type"
Output:
end
p key if key.start_with?('C')
res.each_capitalized_name do |key|
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
Calls the block with each capitalized field name:
def each_capitalized_name #:yield: +key+ block_given? or return enum_for(__method__) { @header.size } @header.each_key do |k| yield capitalize(k) end end
def each_header #:yield: +key+, +value+
Gem::Net::HTTPHeader#each is an alias for Gem::Net::HTTPHeader#each_header.
Returns an enumerator if no block is given.
["cf-ray", "771d17e9bc542cf5-ORD"]
["cf-cache-status", "HIT"]
["cache-control", "max-age=43200"]
["connection", "keep-alive"]
["content-type", "application/json; charset=utf-8"]
Output:
end
p [key, value] if key.start_with?('c')
res.each_header do |key, value|
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
Calls the block with each key/value pair:
def each_header #:yield: +key+, +value+ block_given? or return enum_for(__method__) { @header.size } @header.each do |k,va| yield k, va.join(', ') end end
def each_name(&block) #:yield: +key+
Gem::Net::HTTPHeader#each_name is an alias for Gem::Net::HTTPHeader#each_key.
Returns an enumerator if no block is given.
"cf-ray"
"cf-cache-status"
"cache-control"
"connection"
"content-type"
Output:
end
p key if key.start_with?('c')
res.each_key do |key|
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
Calls the block with each field key:
def each_name(&block) #:yield: +key+ block_given? or return enum_for(__method__) { @header.size } @header.each_key(&block) end
def each_value #:yield: +value+
Returns an enumerator if no block is given.
"cloudflare"
"cf-q-config;dur=6.0000002122251e-06"
"chunked"
Output:
end
p value if value.start_with?('c')
res.each_value do |value|
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
Calls the block with each string field value:
def each_value #:yield: +value+ block_given? or return enum_for(__method__) { @header.size } @header.each_value do |va| yield va.join(', ') end end
def fetch(key, *args, &block) #:yield: +key+
res.fetch('Nosuch') # Raises KeyError.
res.fetch('Nosuch', 'Foo') # => "Foo"
res.fetch('Connection', 'Foo') # => "keep-alive"
otherwise raises an exception:
otherwise, returns +default_val+ if it was given;
With no block, returns the string value for +key+ if it exists;
end # => "nosuch"
value.downcase
res.fetch('Nosuch') do |value|
# Field does not exist; block called.
end # => "keep-alive"
fail 'Cannot happen'
res.fetch('Connection') do |value|
# Field exists; block not called.
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
ignores the +default_val+;
otherwise returns the value of the block;
With a block, returns the string value for +key+ if it exists;
fetch(key, default_val = nil) -> value or default_val
fetch(key, default_val = nil) {|key| ... } -> object
call-seq:
def fetch(key, *args, &block) #:yield: +key+ a = @header.fetch(key.downcase.to_s, *args, &block) a.kind_of?(Array) ? a.join(', ') : a end
def get_fields(key)
res.get_fields('Nosuch') # => nil
res.get_fields('Connection') # => ["keep-alive"]
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
or +nil+ if there is no such field;
Returns the array field value for the given +key+,
def get_fields(key) stringified_downcased_key = key.downcase.to_s return nil unless @header[stringified_downcased_key] @header[stringified_downcased_key].dup end
def initialize_http_header(initheader) #:nodoc:
def initialize_http_header(initheader) #:nodoc: @header = {} return unless initheader initheader.each do |key, value| warn "net/http: duplicated HTTP header: #{key}", uplevel: 3 if key?(key) and $VERBOSE if value.nil? warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE else value = value.strip # raise error for invalid byte sequences if key.to_s.bytesize > MAX_KEY_LENGTH raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..." end if value.to_s.bytesize > MAX_FIELD_LENGTH raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}" end if value.count("\r\n") > 0 raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF" end @header[key.downcase.to_s] = [value] end end end
def key?(key)
req.key?('Nosuch') # => false
req.key?('Accept') # => true
req = Gem::Net::HTTP::Get.new(uri)
Returns +true+ if the field for the case-insensitive +key+ exists, +false+ otherwise:
def key?(key) @header.key?(key.downcase.to_s) end
def main_type
res.main_type # => "application"
res['content-type'] # => "application/json; charset=utf-8"
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
or +nil+ if no such field exists;
from the value of field 'Content-Type',
{media type}[https://en.wikipedia.org/wiki/Media_type]
Returns the leading ('type') part of the
def main_type return nil unless @header['content-type'] self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip end
def proxy_basic_auth(account, password)
# => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
req['Proxy-Authorization']
req.proxy_basic_auth('my_account', 'my_password')
+account+ and +password+ strings:
Sets header 'Proxy-Authorization' using the given
def proxy_basic_auth(account, password) @header['proxy-authorization'] = [basic_encode(account, password)] end
def range
req.range # # => nil
req.delete('Range')
req.range # => [0..99, 200..299, 400..499]
req['Range'] = 'bytes=0-99,200-299,400-499'
req = Gem::Net::HTTP::Get.new(uri)
see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
or +nil+ if there is no such field;
the value of field 'Range',
Returns an array of Range objects that represent
def range return nil unless @header['range'] value = self['Range'] # byte-range-set = *( "," OWS ) ( byte-range-spec / suffix-byte-range-spec ) # *( OWS "," [ OWS ( byte-range-spec / suffix-byte-range-spec ) ] ) # corrected collected ABNF # http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-19#section-5.4.1 # http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-19#appendix-C # http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-19#section-3.2.5 unless /\Abytes=((?:,[ \t]*)*(?:\d+-\d*|-\d+)(?:[ \t]*,(?:[ \t]*\d+-\d*|-\d+)?)*)\z/ =~ value raise Gem::Net::HTTPHeaderSyntaxError, "invalid syntax for byte-ranges-specifier: '#{value}'" end byte_range_set = $1 result = byte_range_set.split(/,/).map {|spec| m = /(\d+)?\s*-\s*(\d+)?/i.match(spec) or raise Gem::Net::HTTPHeaderSyntaxError, "invalid byte-range-spec: '#{spec}'" d1 = m[1].to_i d2 = m[2].to_i if m[1] and m[2] if d1 > d2 raise Gem::Net::HTTPHeaderSyntaxError, "last-byte-pos MUST greater than or equal to first-byte-pos but '#{spec}'" end d1..d2 elsif m[1] d1..-1 elsif m[2] -d2..-1 else raise Gem::Net::HTTPHeaderSyntaxError, 'range is not specified' end } # if result.empty? # byte-range-set must include at least one byte-range-spec or suffix-byte-range-spec # but above regexp already denies it. if result.size == 1 && result[0].begin == 0 && result[0].end == -1 raise Gem::Net::HTTPHeaderSyntaxError, 'only one suffix-byte-range-spec with zero suffix-length' end result end
def range_length
res.range_length # => 500
res['Content-Range'] = 'bytes 0-499/1000'
res['Content-Range'] # => nil
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
'Content-Range', or +nil+ if no such field exists;
Returns the integer representing length of the value of field
def range_length r = content_range() or return nil r.end - r.begin + 1 end
def set_content_type(type, params = {})
req.set_content_type('application/json') # => ["application/json"]
req = Gem::Net::HTTP::Get.new(uri)
see {Content-Type request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-request-header]:
returns the new value;
Sets the value of field 'Content-Type';
def set_content_type(type, params = {}) @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')] end
def set_field(key, val)
def set_field(key, val) al numerable = [] nd_field_value(ary, val) der[key.downcase.to_s] = ary = val.to_s # for compatibility use to_s instead of to_str al.b.count("\r\n") > 0 ise ArgumentError, 'header field value cannot include CR/LF' der[key.downcase.to_s] = [val]
def set_form(params, enctype='application/x-www-form-urlencoded', formopt={})
Field names and values of non-file fields should be encoded with this charset.
- +:charset+: Value is the character set for the form submission.
See {Boundary}[https://www.rfc-editor.org/rfc/rfc7578#section-4.1].
If not given, the boundary is a random string.
- +:boundary+: The value is the boundary string for the multipart message.
that may include the following entries:
is 'multipart/form-data')
(applicable only when argument +enctype+
Optional argument +formopt+ is a hash of options
see {RFC 7578}[https://www.rfc-editor.org/rfc/rfc7578].
- 'multipart/form-data';
- 'application/x-www-form-urlencoded' (the default).
to field 'Content-Type', and must be one of:
Optional argument +enctype+ specifies the value to be given
req.set_form({'foo' => nil, 'bar' => 1, 'file' => file})
# Mixture of fields.
req.set_form({'file' => StringIO.new('Ruby is cool.')})
require 'stringio'
# IO-valued field.
req.set_form({'foo' => 0, 'bar' => 1, 'baz' => 2})
# String-valued fields.
req.set_form({'foo' => nil, 'bar' => nil, 'baz' => nil})
# Nil-valued fields.
Examples:
'multipart/form-data').
(only when argument +enctype+ -- see below -- is given as
- An IO stream opened for reading
- Another string.
- +nil+.
- The value may be:
- The name is a string.
each of its entries is a name/value pair that defines a field:
When +params+ is a hash,
Argument +params+ As a Hash
req.set_form(['foo', %w[bar 1], ['file', file]])
The various forms may be mixed:
req.set_form([['file', file, {filename: "other-filename.foo"}]]
Example:
- +:content_type+: The content type of the uploaded file.
- +:filename+: The name of the file to use.
and an options hash, which may contain these entries:
- A string name, an IO stream opened for reading,
req.set_form([['file', StringIO.new('Ruby is cool.')]])
require 'stringio'
- A string name and an IO stream opened for reading:
'multipart/form-data':
- When argument +enctype+ (see below) is given as
req.set_form([%w[foo 0], %w[bar 1], %w[baz 2]])
- Two strings:
req.set_form([['foo'], ['bar'], ['baz']])
- One string:
the subarray may contain:
each of its elements is a subarray that defines a field;
When +params+ is an array,
Argument +params+ As an Array
req = Gem::Net::HTTP::Post.new(_uri)
_uri.path ='/posts'
_uri = uri.dup
First, we set up a request:
and is often an array or hash.
(method params.map will be called),
{Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes]
Argument +params+ should be an
- An IO stream opened for reading.
- A name/value pair.
- A scalar value.
each field is:
The form data given in +params+ consists of zero or more fields;
Stores form data to be used in a +POST+ or +PUT+ request.
def set_form(params, enctype='application/x-www-form-urlencoded', formopt={}) @body_data = params @body = nil @body_stream = nil @form_option = formopt case enctype when /\Aapplication\/x-www-form-urlencoded\z/i, /\Amultipart\/form-data\z/i self.content_type = enctype else raise ArgumentError, "invalid enctype: #{enctype}" end end
def set_form_data(params, sep = '&')
req.body # => "q=ruby|lang=en"
req.set_form_data({q: 'ruby', lang: 'en'}, '|')
uses that string as the separator:
With string argument +sep+ also given,
req.body # => "q=ruby&q=perl&lang=en"
req.set_form_data([['q', 'ruby'], ['q', 'perl'], ['lang', 'en']])
req.body # => "q=ruby&q=perl&lang=en"
req.set_form_data(q: ['ruby', 'perl'], lang: 'en')
req.body # => "q=ruby&lang=en"
req.set_form_data([['q', 'ruby'], ['lang', 'en']])
req['Content-Type'] # => "application/x-www-form-urlencoded"
req.body # => "q=ruby&lang=en"
req.set_form_data(q: 'ruby', lang: 'en')
req = Gem::Net::HTTP::Post.new('example.com')
sets the body to a URL-encoded string with the default separator '&':
With only argument +params+ given,
{Gem::URI.encode_www_form}[https://docs.ruby-lang.org/en/master/Gem::URI.html#method-c-encode_www_form].
Argument +params+ must be suitable for use as argument +enum+ to
The resulting request is suitable for HTTP request +POST+ or +PUT+.
to 'application/x-www-form-urlencoded'.
and sets request header field 'Content-Type'
Sets the request body to a URL-encoded string derived from argument +params+,
def set_form_data(params, sep = '&') query = Gem::URI.encode_www_form(params) query.gsub!(/&/, sep) if sep != '&' self.body = query self.content_type = 'application/x-www-form-urlencoded' end
def set_range(r, e = nil)
req['Range'] # => "bytes=100-199"
req.set_range(100..199) # => 100..199
With argument +range+:
req['Range'] # => "bytes=100-199"
req.set_range(100, 100) # => 100...200
With arguments +offset+ and +length+:
req['Range'] # => "bytes=0-99"
req.set_range(100) # => 100
req = Gem::Net::HTTP::Get.new(uri)
With argument +length+:
see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
Sets the value for field 'Range';
set_range(begin..length) -> range
set_range(offset, length) -> range
set_range(length) -> length
call-seq:
def set_range(r, e = nil) unless r @header.delete 'range' return r end r = (r...r+e) if e case r when Numeric n = r.to_i rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}") when Range first = r.first last = r.end last -= 1 if r.exclude_end? if last == -1 rangestr = (first > 0 ? "#{first}-" : "-#{-first}") else raise Gem::Net::HTTPHeaderSyntaxError, 'range.first is negative' if first < 0 raise Gem::Net::HTTPHeaderSyntaxError, 'range.last is negative' if last < 0 raise Gem::Net::HTTPHeaderSyntaxError, 'must be .first < .last' if first > last rangestr = "#{first}-#{last}" end else raise TypeError, 'Range/Integer is required' end @header['range'] = ["bytes=#{rangestr}"] r end
def size #:nodoc: obsolete
def size #:nodoc: obsolete @header.size end
def sub_type
res.sub_type # => "json"
res['content-type'] # => "application/json; charset=utf-8"
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
or +nil+ if no such field exists;
from the value of field 'Content-Type',
{media type}[https://en.wikipedia.org/wiki/Media_type]
Returns the trailing ('subtype') part of the
def sub_type return nil unless @header['content-type'] _, sub = *self['Content-Type'].split(';').first.to_s.split('/') return nil unless sub sub.strip end
def to_hash
"host"=>["jsonplaceholder.typicode.com"]}
"user-agent"=>["Ruby"],
"accept"=>["*/*"],
{"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
# =>
req.to_hash
req = Gem::Net::HTTP::Get.new(uri)
Returns a hash of the key/value pairs:
def to_hash @header.dup end
def type_params
res.type_params # => {"charset"=>"utf-8"}
res['content-type'] # => "application/json; charset=utf-8"
res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
or +nil+ if no such field exists;
Returns the trailing ('parameters') part of the value of field 'Content-Type',
def type_params result = {} list = self['Content-Type'].to_s.split(';') list.shift list.each do |param| k, v = *param.split('=', 2) result[k.strip] = v.strip end result end