Class: Net::HTTPGenericRequest
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
|
|
Super Chains via Extension / Inclusion / Inheritance | |
Instance Chain:
self,
HTTPHeader
|
|
Inherits: | Object |
Defined in: | lib/net/http/generic_request.rb |
Overview
HTTPGenericRequest is the parent of the HTTPRequest
class.
Do not use this directly; instead, use a subclass of HTTPRequest
.
About the Examples
Examples here assume that net/http
has been required (which also requires #uri):
require 'net/http'
Many code examples here use these example websites:
Some examples also assume these variables:
uri = URI('https://jsonplaceholder.typicode.com/')
uri.freeze # Examples may not modify.
hostname = uri.hostname # => "jsonplaceholder.typicode.com"
path = uri.path # => "/"
port = uri.port # => 443
So that example requests may be written as:
Net::HTTP.get(uri)
Net::HTTP.get(hostname, '/index.html')
Net::HTTP.start(hostname) do |http|
http.get(‘/todos/1’) http.get(‘/todos/2’)
end
An example that needs a modified URI first duplicates #uri, then modifies the duplicate:
_uri = uri.dup
_uri.path = '/todos/1'
Constant Summary
HTTPHeader
- Included
Class Method Summary
- .new(m, reqbody, resbody, uri_or_path, initheader = nil) ⇒ HTTPGenericRequest constructor Internal use only
Instance Attribute Summary
-
#body
rw
Returns the string body for the request, or
nil
if there is none: -
#body=(str)
rw
Sets the body for the request:
-
#body_stream
rw
Returns the body stream object for the request, or
nil
if there is none: -
#body_stream=(input)
rw
Sets the body stream for the request:
-
#decode_content
readonly
Returns
false
if the request’s header'Accept-Encoding'
has been set manually or deleted (indicating that the user intends to handle encoding in the response),true
otherwise: -
#method
readonly
Returns the string method name for the request:
-
#path
readonly
Returns the string path for the request:
-
#request_body_permitted? ⇒ Boolean
readonly
Returns whether the request may have a body:
-
#response_body_permitted? ⇒ Boolean
readonly
Returns whether the response may have a body:
-
#uri
readonly
Returns the URI object for the request, or
nil
if none: - #body_exist? ⇒ Boolean readonly Internal use only
HTTPHeader
- Included
#chunked? | Returns |
#connection_close? | Returns whether the |
#connection_keep_alive? | Returns whether the |
#content_length | Returns the value of field |
#content_length= | Sets the value of field |
Instance Method Summary
- #encode_multipart_form_data(out, params, opt)
- #flush_buffer(out, buf, chunked_p)
-
#inspect
Returns a string representation of the request:
- #quote_string(str, charset)
- #send_request_with_body(sock, ver, path, body)
- #send_request_with_body_data(sock, ver, path, params)
- #send_request_with_body_stream(sock, ver, path, f)
- #supply_default_content_type
-
#wait_for_continue(sock, ver)
Waits up to the continue timeout for a response from the server provided we’re speaking
HTTP
1.1 and are expecting a 100-continue response. - #write_header(sock, ver, path)
-
#[]=(key, val)
Internal use only
Don’t automatically decode response content-encoding if the user indicates they want to handle it.
-
#exec(sock, ver, path)
Internal use only
write.
-
#set_body_internal(str)
Internal use only
internal use only.
-
#update_uri(addr, port, ssl)
Internal use only
internal use only.
HTTPHeader
- Included
#[] | Returns the string field value for the case-insensitive field |
#[]= | Sets the value for the case-insensitive |
#add_field | Adds value |
#basic_auth | Sets header |
#canonical_each | Alias for HTTPHeader#each_capitalized. |
#content_range | Returns a Range object representing the value of field |
#content_type | Returns the media type from the value of field |
#content_type= | Alias for HTTPHeader#set_content_type. |
#delete | Removes the header for the given case-insensitive |
#each | Alias for HTTPHeader#each_header. |
#each_capitalized | Like |
#each_capitalized_name | Calls the block with each capitalized field name: |
#each_header | Calls the block with each key/value pair: |
#each_key | Alias for HTTPHeader#each_name. |
#each_name | Calls the block with each field key: |
#each_value | Calls the block with each string field value: |
#fetch | With a block, returns the string value for |
#form_data= | Alias for HTTPHeader#set_form_data. |
#get_fields | Returns the array field value for the given |
#key? | Returns |
#main_type | Returns the leading (‘type’) part of the media type from the value of field |
#proxy_basic_auth | Sets header |
#range | Returns an array of Range objects that represent the value of field |
#range= | Alias for HTTPHeader#set_range. |
#range_length | Returns the integer representing length of the value of field |
#set_content_type | Sets the value of field |
#set_form | Stores form data to be used in a |
#set_form_data | Sets the request body to a URL-encoded string derived from argument |
#set_range | Sets the value for field |
#sub_type | Returns the trailing (‘subtype’) part of the media type from the value of field |
#to_hash | Returns a hash of the key/value pairs: |
#type_params | Returns the trailing (‘parameters’) part of the value of field |
#append_field_value, #basic_encode, #capitalize, #set_field, #initialize_http_header, | |
#length | Alias for HTTPHeader#size. |
#size | obsolete. |
Constructor Details
.new(m, reqbody, resbody, uri_or_path, initheader = nil) ⇒ HTTPGenericRequest
# File 'lib/net/http/generic_request.rb', line 15
def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: @method = m @request_has_body = reqbody @response_has_body = resbody if URI === uri_or_path then raise ArgumentError, "not an HTTP URI" unless URI::HTTP === uri_or_path hostname = uri_or_path.hostname raise ArgumentError, "no host component for URI" unless (hostname && hostname.length > 0) @uri = uri_or_path.dup host = @uri.hostname.dup host << ":" << @uri.port.to_s if @uri.port != @uri.default_port @path = uri_or_path.request_uri raise ArgumentError, "no HTTP request path given" unless @path else @uri = nil host = nil raise ArgumentError, "no HTTP request path given" unless uri_or_path raise ArgumentError, "HTTP request path is empty" if uri_or_path.empty? @path = uri_or_path.dup end @decode_content = false if Net::HTTP::HAVE_ZLIB then if !initheader || !initheader.keys.any? { |k| %w[accept-encoding range].include? k.downcase } then @decode_content = true if @response_has_body initheader = initheader ? initheader.dup : {} initheader["accept-encoding"] = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" end end initialize_http_header initheader self['Accept'] ||= '*/*' self['User-Agent'] ||= 'Ruby' self['Host'] ||= host if host @body = nil @body_stream = nil @body_data = nil end
Instance Attribute Details
#body (rw)
# File 'lib/net/http/generic_request.rb', line 145
attr_reader :body
#body=(str) (rw)
# File 'lib/net/http/generic_request.rb', line 154
def body=(str) @body = str @body_stream = nil @body_data = nil str end
#body_exist? ⇒ Boolean
(readonly)
# File 'lib/net/http/generic_request.rb', line 133
def body_exist? # :nodoc: warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?", uplevel: 1 if $VERBOSE response_body_permitted? end
#body_stream (rw)
Returns the body stream object for the request, or nil
if there is none:
req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
req.body_stream # => nil
require 'stringio'
req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
req.body_stream # => #<StringIO:0x0000027d1e5affa8>
# File 'lib/net/http/generic_request.rb', line 169
attr_reader :body_stream
#body_stream=(input) (rw)
Sets the body stream for the request:
req = Net::HTTP::Post.new(uri) # => #<Net::HTTP::Post POST>
req.body_stream # => nil
require 'stringio'
req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
req.body_stream # => #<StringIO:0x0000027d1e5affa8>
# File 'lib/net/http/generic_request.rb', line 179
def body_stream=(input) @body = nil @body_stream = input @body_data = nil input end
#decode_content (readonly)
Returns false
if the request’s header 'Accept-Encoding'
has been set manually or deleted (indicating that the user intends to handle encoding in the response), true
otherwise:
req = Net::HTTP::Get.new(uri) # => #<Net::HTTP::Get GET>
req['Accept-Encoding'] # => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
req.decode_content # => true
req['Accept-Encoding'] = 'foo'
req.decode_content # => false
req.delete('Accept-Encoding')
req.decode_content # => false
# File 'lib/net/http/generic_request.rb', line 95
attr_reader :decode_content
#method (readonly)
# File 'lib/net/http/generic_request.rb', line 65
attr_reader :method
#path (readonly)
# File 'lib/net/http/generic_request.rb', line 72
attr_reader :path
#request_body_permitted? ⇒ Boolean
(readonly)
# File 'lib/net/http/generic_request.rb', line 120
def request_body_permitted? @request_has_body end
#response_body_permitted? ⇒ Boolean
(readonly)
# File 'lib/net/http/generic_request.rb', line 129
def response_body_permitted? @response_has_body end
#uri (readonly)
# File 'lib/net/http/generic_request.rb', line 80
attr_reader :uri
Instance Method Details
#[]=(key, val)
Don’t automatically decode response content-encoding if the user indicates they want to handle it.
# File 'lib/net/http/generic_request.rb', line 109
def []=(key, val) # :nodoc: @decode_content = false if key.downcase == 'accept-encoding' super key, val end
#encode_multipart_form_data(out, params, opt)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 312
def encode_multipart_form_data(out, params, opt) charset = opt[:charset] boundary = opt[:boundary] require 'securerandom' unless defined?(SecureRandom) boundary ||= SecureRandom.urlsafe_base64(40) chunked_p = chunked? buf = +'' params.each do |key, value, h={}| key = quote_string(key, charset) filename = h.key?(:filename) ? h[:filename] : value.respond_to?(:to_path) ? File.basename(value.to_path) : nil buf << "--#{boundary}\r\n" if filename filename = quote_string(filename, charset) type = h[:content_type] || 'application/octet-stream' buf << "Content-Disposition: form-data; " \ "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \ "Content-Type: #{type}\r\n\r\n" if !out.respond_to?(:write) || !value.respond_to?(:read) # if out is not an IO or value is not an IO buf << (value.respond_to?(:read) ? value.read : value) elsif value.respond_to?(:size) && chunked_p # if out is an IO and value is a File, use IO.copy_stream flush_buffer(out, buf, chunked_p) out << "%x\r\n" % value.size if chunked_p IO.copy_stream(value, out) out << "\r\n" if chunked_p else # out is an IO, and value is not a File but an IO flush_buffer(out, buf, chunked_p) 1 while flush_buffer(out, value.read(4096), chunked_p) end else # non-file field: # HTML5 says, "The parts of the generated multipart/form-data # resource that correspond to non-file fields must not have a # Content-Type header specified." buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n" buf << (value.respond_to?(:read) ? value.read : value) end buf << "\r\n" end buf << "--#{boundary}--\r\n" flush_buffer(out, buf, chunked_p) out << "0\r\n\r\n" if chunked_p end
#exec(sock, ver, path)
write
# File 'lib/net/http/generic_request.rb', line 198
def exec(sock, ver, path) #:nodoc: internal use only if @body send_request_with_body sock, ver, path, @body elsif @body_stream send_request_with_body_stream sock, ver, path, @body_stream elsif @body_data send_request_with_body_data sock, ver, path, @body_data else write_header sock, ver, path end end
#flush_buffer(out, buf, chunked_p)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 368
def flush_buffer(out, buf, chunked_p) return unless buf out << "%x\r\n"%buf.bytesize if chunked_p out << buf out << "\r\n" if chunked_p buf.clear end
#inspect
# File 'lib/net/http/generic_request.rb', line 101
def inspect "\#<#{self.class} #{@method}>" end
#quote_string(str, charset)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 363
def quote_string(str, charset) str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset str.gsub(/[\\"]/, '\\\\\&') end
#send_request_with_body(sock, ver, path, body)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 260
def send_request_with_body(sock, ver, path, body) self.content_length = body.bytesize delete 'Transfer-Encoding' supply_default_content_type write_header sock, ver, path wait_for_continue sock, ver if sock.continue_timeout sock.write body end
#send_request_with_body_data(sock, ver, path, params)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 286
def send_request_with_body_data(sock, ver, path, params) if /\Amultipart\/form-data\z/i !~ self.content_type self.content_type = 'application/x-www-form-urlencoded' return send_request_with_body(sock, ver, path, URI.encode_www_form(params)) end opt = @form_option.dup require 'securerandom' unless defined?(SecureRandom) opt[:boundary] ||= SecureRandom.urlsafe_base64(40) self.set_content_type(self.content_type, boundary: opt[:boundary]) if chunked? write_header sock, ver, path encode_multipart_form_data(sock, params, opt) else require 'tempfile' file = Tempfile.new('multipart') file.binmode encode_multipart_form_data(file, params, opt) file.rewind self.content_length = file.size write_header sock, ver, path IO.copy_stream(file, sock) file.close(true) end end
#send_request_with_body_stream(sock, ver, path, f)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 269
def send_request_with_body_stream(sock, ver, path, f) unless content_length() or chunked? raise ArgumentError, "Content-Length not given and Transfer-Encoding is not `chunked'" end supply_default_content_type write_header sock, ver, path wait_for_continue sock, ver if sock.continue_timeout if chunked? chunker = Chunker.new(sock) IO.copy_stream(f, chunker) chunker.finish else IO.copy_stream(f, sock) end end
#set_body_internal(str)
internal use only
# File 'lib/net/http/generic_request.rb', line 186
def set_body_internal(str) #:nodoc: internal use only raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream) self.body = str if str if @body.nil? && @body_stream.nil? && @body_data.nil? && request_body_permitted? self.body = '' end end
#supply_default_content_type
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 376
def supply_default_content_type return if content_type() warn 'net/http: Content-Type did not set; using application/x-www-form-urlencoded', uplevel: 1 if $VERBOSE set_content_type 'application/x-www-form-urlencoded' end
#update_uri(addr, port, ssl)
internal use only
# File 'lib/net/http/generic_request.rb', line 210
def update_uri(addr, port, ssl) # :nodoc: internal use only # reflect the connection and @path to @uri return unless @uri if ssl scheme = 'https' klass = URI::HTTPS else scheme = 'http' klass = URI::HTTP end if host = self['host'] host.sub!(/:.*/m, '') elsif host = @uri.host else host = addr end # convert the class of the URI if @uri.is_a?(klass) @uri.host = host @uri.port = port else @uri = klass.new( scheme, @uri.userinfo, host, port, nil, @uri.path, nil, @uri.query, nil) end end
#wait_for_continue(sock, ver)
Waits up to the continue timeout for a response from the server provided we’re speaking HTTP
1.1 and are expecting a 100-continue response.
# File 'lib/net/http/generic_request.rb', line 386
def wait_for_continue(sock, ver) if ver >= '1.1' and @header['expect'] and @header['expect'].include?('100-continue') if sock.io.to_io.wait_readable(sock.continue_timeout) res = Net::HTTPResponse.read_new(sock) unless res.kind_of?(Net::HTTPContinue) res.decode_content = @decode_content throw :response, res end end end end
#write_header(sock, ver, path)
[ GitHub ]# File 'lib/net/http/generic_request.rb', line 399
def write_header(sock, ver, path) reqline = "#{@method} #{path} HTTP/#{ver}" if /[\r\n]/ =~ reqline raise ArgumentError, "A Request-Line must not contain CR or LF" end buf = +'' buf << reqline << "\r\n" each_capitalized do |k,v| buf << "#{k}: #{v}\r\n" end buf << "\r\n" sock.write buf end