Class: Net::HTTPResponse
Overview
HTTP
response class.
This class wraps together the response header and the response body (the entity requested).
It mixes in the HTTPHeader
module, which provides access to response header values both via hash-like methods and via individual readers.
Note that each possible HTTP
response code defines its own HTTPResponse
subclass. All classes are defined under the ::Net
module. Indentation indicates inheritance. For a list of the classes see HTTP
.
Correspondence HTTP code => class
is stored in CODE_TO_OBJ constant:
Net::HTTPResponse::CODE_TO_OBJ['404'] #=> Net::HTTPNotFound
Constant Summary
-
CODE_CLASS_TO_OBJ =
# File 'lib/net/http/responses.rb', line 239{ '1' => Net::HTTPInformation, '2' => Net::HTTPSuccess, '3' => Net::HTTPRedirection, '4' => Net::HTTPClientError, '5' => Net::HTTPServerError }
-
CODE_TO_OBJ =
# File 'lib/net/http/responses.rb', line 246{ '100' => Net::HTTPContinue, '101' => Net::HTTPSwitchProtocol, '102' => Net::HTTPProcessing, '103' => Net::HTTPEarlyHints, '200' => Net::HTTPOK, '201' => Net::HTTPCreated, '202' => Net::HTTPAccepted, '203' => Net::HTTPNonAuthoritativeInformation, '204' => Net::HTTPNoContent, '205' => Net::HTTPResetContent, '206' => Net::HTTPPartialContent, '207' => Net::HTTPMultiStatus, '208' => Net::HTTPAlreadyReported, '226' => Net::HTTPIMUsed, '300' => Net::HTTPMultipleChoices, '301' => Net::HTTPMovedPermanently, '302' => Net::HTTPFound, '303' => Net::HTTPSeeOther, '304' => Net::HTTPNotModified, '305' => Net::HTTPUseProxy, '307' => Net::HTTPTemporaryRedirect, '308' => Net::HTTPPermanentRedirect, '400' => Net::HTTPBadRequest, '401' => Net::HTTPUnauthorized, '402' => Net::HTTPPaymentRequired, '403' => Net::HTTPForbidden, '404' => Net::HTTPNotFound, '405' => Net::HTTPMethodNotAllowed, '406' => Net::HTTPNotAcceptable, '407' => Net::HTTPProxyAuthenticationRequired, '408' => Net::HTTPRequestTimeout, '409' => Net::HTTPConflict, '410' => Net::HTTPGone, '411' => Net::HTTPLengthRequired, '412' => Net::HTTPPreconditionFailed, '413' => Net::HTTPPayloadTooLarge, '414' => Net::HTTPURITooLong, '415' => Net::HTTPUnsupportedMediaType, '416' => Net::HTTPRangeNotSatisfiable, '417' => Net::HTTPExpectationFailed, '421' => Net::HTTPMisdirectedRequest, '422' => Net::HTTPUnprocessableEntity, '423' => Net::HTTPLocked, '424' => Net::HTTPFailedDependency, '426' => Net::HTTPUpgradeRequired, '428' => Net::HTTPPreconditionRequired, '429' => Net::HTTPTooManyRequests, '431' => Net::HTTPRequestHeaderFieldsTooLarge, '451' => Net::HTTPUnavailableForLegalReasons, '500' => Net::HTTPInternalServerError, '501' => Net::HTTPNotImplemented, '502' => Net::HTTPBadGateway, '503' => Net::HTTPServiceUnavailable, '504' => Net::HTTPGatewayTimeout, '505' => Net::HTTPVersionNotSupported, '506' => Net::HTTPVariantAlsoNegotiates, '507' => Net::HTTPInsufficientStorage, '508' => Net::HTTPLoopDetected, '510' => Net::HTTPNotExtended, '511' => Net::HTTPNetworkAuthenticationRequired, }
Class Attribute Summary
-
.body_permitted? ⇒ Boolean
readonly
true if the response has a body.
Class Method Summary
- .each_response_header(sock) {|key, value| ... } private
- .read_status_line(sock) private
- .response_class(code) private
-
.exception_type
Internal use only
internal use only.
-
.new(httpv, code, msg) ⇒ HTTPResponse
constructor
Internal use only
internal use only.
-
.read_new(sock)
Internal use only
internal use only.
Instance Attribute Summary
-
#body
(also: #entity)
rw
Returns the full entity body.
-
#body=(value)
rw
Because it may be necessary to modify the body, Eg, decompression this method facilitates that.
-
#body_encoding
rw
The encoding to use for the response body.
-
#body_encoding=(value)
rw
Set the encoding to use for the response body.
-
#code
readonly
The HTTP result code string.
-
#decode_content
rw
Set to true automatically when the request did not contain an Accept-Encoding header from the user.
-
#http_version
readonly
The HTTP version supported by the server.
-
#ignore_eof
rw
Whether to ignore EOF when reading bodies with a specified Content-Length header.
-
#message
(also: #msg)
readonly
The HTTP result message sent by the server.
-
#uri
rw
The URI used to fetch this response.
-
#entity
readonly
Internal use only
Alias for #body.
-
#msg
readonly
Internal use only
Alias for #message.
- #uri=(uri) rw Internal use only
HTTPHeader
- Included
#chunked? | Returns “true” if the “transfer-encoding” header is present and set to “chunked”. |
#connection_close?, #connection_keep_alive?, | |
#content_length | Returns an Integer object which represents the |
#content_length= |
Instance Method Summary
- #inspect
-
#read_body(dest = nil, &block)
Gets the entity body returned by the remote
HTTP
server. -
#value
Raises an
HTTP
error if the response is not 2xx (success). - #extracting_encodings_from_meta_elements(value) private
- #get_attribute(ss) private
- #procdest(dest, block) private
- #read_body_0(dest) private
- #stream_check private
-
#code_type
Internal use only
response <-> exception relationship.
- #error! Internal use only
- #error_type Internal use only
- #header Internal use only
- #read_header Internal use only
-
#reading_body(sock, reqmethodallowbody)
Internal use only
body.
-
#response
Internal use only
header (for backward compatibility only; DO NOT USE).
- #check_bom(str) private Internal use only
- #detect_encoding(str, encoding = nil) private Internal use only
-
#inflater
private
Internal use only
Checks for a supported Content-Encoding header and yields an Inflate wrapper for this response’s socket when zlib is present.
-
#read_chunked(dest, chunk_data_io)
private
Internal use only
read_chunked reads from @socket for chunk-size, chunk-extension, CRLF, etc.
- #scanning_meta(str) private Internal use only
- #sniff_encoding(str, encoding = nil) private Internal use only
HTTPHeader
- Included
#[] | Returns the header field corresponding to the case-insensitive key. |
#[]= | Sets the header field corresponding to the case-insensitive key. |
#add_field |
|
#basic_auth | Set the Authorization: header for “Basic” authorization. |
#canonical_each | Alias for HTTPHeader#each_capitalized. |
#content_range | Returns a Range object which represents the value of the Content-Range: header field. |
#content_type | Returns a content type string such as “text/html”. |
#content_type= | Alias for HTTPHeader#set_content_type. |
#delete | Removes a header field, specified by case-insensitive key. |
#each | Alias for HTTPHeader#each_header. |
#each_capitalized | As for |
#each_capitalized_name | Iterates through the header names in the header, passing capitalized header names to the code block. |
#each_header | Iterates through the header names and values, passing in the name and value to the code block supplied. |
#each_key | Alias for HTTPHeader#each_name. |
#each_name | Iterates through the header names in the header, passing each header name to the code block. |
#each_value | Iterates through header values, passing each value to the code block. |
#fetch | Returns the header field corresponding to the case-insensitive key. |
#form_data= | Alias for HTTPHeader#set_form_data. |
#get_fields |
|
#initialize_http_header, | |
#key? | true if |
#main_type | Returns a content type string such as “text”. |
#proxy_basic_auth | Set Proxy-Authorization: header for “Basic” authorization. |
#range | Returns an Array of Range objects which represent the Range: |
#range= | Alias for HTTPHeader#set_range. |
#range_length | The length of the range represented in Content-Range: header. |
#set_content_type | Sets the content type in an |
#set_form | Set an HTML form data set. |
#set_form_data | Set header fields and a body from HTML form data. |
#set_range | Sets the |
#sub_type | Returns a content type string such as “html”. |
#to_hash | Returns a Hash consisting of header names and array of values. |
#type_params | Any parameters specified for the content type, returned as a Hash. |
#append_field_value, #basic_encode, #capitalize, #set_field, | |
#length | Alias for HTTPHeader#size. |
#size | obsolete. |
Constructor Details
.new(httpv, code, msg) ⇒ HTTPResponse
internal use only
Class Attribute Details
.body_permitted? ⇒ Boolean
(readonly)
true if the response has a body.
# File 'lib/net/http/response.rb', line 22
def body_permitted? self::HAS_BODY end
Class Method Details
.each_response_header(sock) {|key, value| ... } (private)
# File 'lib/net/http/response.rb', line 54
def each_response_header(sock) key = value = nil while true line = sock.readuntil("\n", true).sub(/\s+\z/, '') break if line.empty? if line[0] == ?\s or line[0] == ?\t and value value << ' ' unless value.empty? value << line.strip else yield key, value if key key, value = line.strip.split(/\s*:\s*/, 2) raise Net::HTTPBadResponse, 'wrong header line format' if value.nil? end end yield key, value if key end
.exception_type
internal use only
# File 'lib/net/http/response.rb', line 26
def exception_type # :nodoc: internal use only self::EXCEPTION_TYPE end
.read_new(sock)
internal use only
# File 'lib/net/http/response.rb', line 30
def read_new(sock) #:nodoc: internal use only httpv, code, msg = read_status_line(sock) res = response_class(code).new(httpv, code, msg) each_response_header(sock) do |k,v| res.add_field k, v end res end
.read_status_line(sock) (private)
[ GitHub ]# File 'lib/net/http/response.rb', line 41
def read_status_line(sock) str = sock.readline m = /\AHTTP(?:\/(\d\.\d))?\s(\d\d\d)(?:\s(.*))?\z/in.match(str) or raise Net::HTTPBadResponse, "wrong status line: #{str.dump}" m.captures end
.response_class(code) (private)
[ GitHub ]# File 'lib/net/http/response.rb', line 48
def response_class(code) CODE_TO_OBJ[code] or CODE_CLASS_TO_OBJ[code[0,1]] or Net::HTTPUnknownResponse end
Instance Attribute Details
#body (rw) Also known as: #entity
Returns the full entity body.
Calling this method a second or subsequent time will return the string already read.
http.request_get(‘/index.html’) {|res|
puts res.body
}
http.request_get(‘/index.html’) {|res|
p res.body.object_id # 538149362
p res.body.object_id # 538149362
}
# File 'lib/net/http/response.rb', line 263
def body read_body() end
#body=(value) (rw)
Because it may be necessary to modify the body, Eg, decompression this method facilitates that.
#body_encoding (rw)
The encoding to use for the response body. If Encoding, use that encoding. If other true value, attempt to detect the appropriate encoding, and use that.
# File 'lib/net/http/response.rb', line 114
attr_reader :body_encoding
#body_encoding=(value) (rw)
Set the encoding to use for the response body. If given a String, find the related Encoding.
#code (readonly)
The HTTP result code string. For example, ‘302’. You can also determine the response type by examining which response subclass the response object is an instance of.
# File 'lib/net/http/response.rb', line 97
attr_reader :code
#decode_content (rw)
Set to true automatically when the request did not contain an Accept-Encoding header from the user.
# File 'lib/net/http/response.rb', line 109
attr_accessor :decode_content
#entity (readonly)
Alias for #body.
# File 'lib/net/http/response.rb', line 273
alias entity body #:nodoc: obsolete
#http_version (readonly)
The HTTP version supported by the server.
# File 'lib/net/http/response.rb', line 92
attr_reader :http_version
#ignore_eof (rw)
Whether to ignore EOF when reading bodies with a specified Content-Length header.
# File 'lib/net/http/response.rb', line 125
attr_accessor :ignore_eof
#message (readonly) Also known as: #msg
The HTTP result message sent by the server. For example, ‘Not Found’.
# File 'lib/net/http/response.rb', line 100
attr_reader :
#msg (readonly)
Alias for #message.
# File 'lib/net/http/response.rb', line 101
alias msg # :nodoc: obsolete
#uri (rw)
The URI used to fetch this response. The response URI is only available if a URI was used to create the request.
# File 'lib/net/http/response.rb', line 105
attr_reader :uri
#uri=(uri) (rw)
Instance Method Details
#check_bom(str) (private)
# File 'lib/net/http/response.rb', line 314
def check_bom(str) case str.byteslice(0, 2) when "\xFE\xFF" return Encoding::UTF_16BE when "\xFF\xFE" return Encoding::UTF_16LE end if "\xEF\xBB\xBF" == str.byteslice(0, 3) return Encoding::UTF_8 end nil end
#code_type
response <-> exception relationship
# File 'lib/net/http/response.rb', line 135
def code_type #:nodoc: self.class end
#detect_encoding(str, encoding = nil) (private)
# File 'lib/net/http/response.rb', line 278
def detect_encoding(str, encoding=nil) if encoding elsif encoding = type_params['charset'] elsif encoding = check_bom(str) else encoding = case content_type&.downcase when %r{text/x(?:ht)?ml|application/(?:[^]\+)?xml} /\A<xml[ \t\r\n]+ version[ \t\r\n]*=[ \t\r\n]*(?:"[0-9.]+"|'[0-9.]*')[ \t\r\n]+ encoding[ \t\r\n]*=[ \t\r\n]* (?:"([A-Za-z][\-A-Za-z0-9._]*)"|'([A-Za-z][\-A-Za-z0-9._]*)')/x =~ str encoding = $1 || $2 || Encoding::UTF_8 when %r{text/html.*} sniff_encoding(str) end end return encoding end
#error!
# File 'lib/net/http/response.rb', line 139
def error! #:nodoc: = @code += ' ' + @message.dump if @message raise error_type().new(, self) end
#error_type
# File 'lib/net/http/response.rb', line 145
def error_type #:nodoc: self.class::EXCEPTION_TYPE end
#extracting_encodings_from_meta_elements(value) (private)
[ GitHub ]#get_attribute(ss) (private)
[ GitHub ]# File 'lib/net/http/response.rb', line 369
def get_attribute(ss) ss.scan(/[\t\n\f\r \/]*/) if ss.peek(1) == '>' ss.getch return nil end name = ss.scan(/[^=\t\n\f\r \/>]*/) name.downcase! raise if name.empty? ss.skip(/[\t\n\f\r ]*/) if ss.getch != '=' value = '' return [name, value] end ss.skip(/[\t\n\f\r ]*/) case ss.peek(1) when '"' ss.getch value = ss.scan(/[^"]+/) value.downcase! ss.getch when "'" ss.getch value = ss.scan(/[^']+/) value.downcase! ss.getch when '>' value = '' else value = ss.scan(/[^\t\n\f\r >]+/) value.downcase! end [name, value] end
#header
# File 'lib/net/http/response.rb', line 167
def header #:nodoc: warn "Net::HTTPResponse#header is obsolete", uplevel: 1 if $VERBOSE self end
#inflater (private)
Checks for a supported Content-Encoding header and yields an Inflate wrapper for this response’s socket when zlib is present. If the Content-Encoding is not supported or zlib is missing, the plain socket is yielded.
If a Content-Range header is present, a plain socket is yielded as the bytes in the range may not be a complete deflate block.
# File 'lib/net/http/response.rb', line 421
def inflater # :nodoc: return yield @socket unless Net::HTTP::HAVE_ZLIB return yield @socket unless @decode_content return yield @socket if self['content-range'] v = self['content-encoding'] case v&.downcase when 'deflate', 'gzip', 'x-gzip' then self.delete 'content-encoding' inflate_body_io = Inflater.new(@socket) begin yield inflate_body_io success = true ensure begin inflate_body_io.finish if self['content-length'] self['content-length'] = inflate_body_io.bytes_inflated.to_s end rescue => err # Ignore #finish's error if there is an exception from yield raise err if success end end when 'none', 'identity' then self.delete 'content-encoding' yield @socket else yield @socket end end
#inspect
[ GitHub ]# File 'lib/net/http/response.rb', line 127
def inspect "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" end
#procdest(dest, block) (private)
#read_body(dest = nil, &block)
Gets the entity body returned by the remote HTTP
server.
If a block is given, the body is passed to the block, and the body is provided in fragments, as it is read in from the socket.
If dest
argument is given, response is read into that variable, with dest#<<
method (it could be String or IO, or any other object responding to <<
).
Calling this method a second or subsequent time for the same HTTPResponse
object will return the value already read.
http.request_get(‘/index.html’) {|res|
puts res.read_body
}
http.request_get(‘/index.html’) {|res|
p res.read_body.object_id # 538149362
p res.read_body.object_id # 538149362
}
# using iterator
http.request_get(‘/index.html’) {|res|
res.read_body do |segment|
print segment
end
}
# File 'lib/net/http/response.rb', line 220
def read_body(dest = nil, &block) if @read raise IOError, "#{self.class}\#read_body called twice" if dest or block return @body end to = procdest(dest, block) stream_check if @body_exist read_body_0 to @body = to else @body = nil end @read = true case enc = @body_encoding when Encoding, false, nil # Encoding: force given encoding # false/nil: do not force encoding else # other value: detect encoding from body enc = detect_encoding(@body) end @body.force_encoding(enc) if enc @body end
#read_body_0(dest) (private)
[ GitHub ]# File 'lib/net/http/response.rb', line 456
def read_body_0(dest) inflater do |inflate_body_io| if chunked? read_chunked dest, inflate_body_io return end @socket = inflate_body_io clen = content_length() if clen @socket.read clen, dest, @ignore_eof return end clen = range_length() if clen @socket.read clen, dest return end @socket.read_all dest end end
#read_chunked(dest, chunk_data_io) (private)
read_chunked reads from @socket for chunk-size, chunk-extension, CRLF, etc. and chunk_data_io
for chunk-data which may be deflate or gzip encoded.
See RFC 2616 section 3.6.1 for definitions
# File 'lib/net/http/response.rb', line 486
def read_chunked(dest, chunk_data_io) # :nodoc: total = 0 while true line = @socket.readline hexlen = line.slice(/[0-9a-fA-F]+/) or raise Net::HTTPBadResponse, "wrong chunk size line: #{line}" len = hexlen.hex break if len == 0 begin chunk_data_io.read len, dest ensure total += len @socket.read 2 # \r\n end end until @socket.readline.empty? # none end end
#read_header
# File 'lib/net/http/response.rb', line 172
def read_header #:nodoc: warn "Net::HTTPResponse#read_header is obsolete", uplevel: 1 if $VERBOSE self end
#reading_body(sock, reqmethodallowbody)
body
# File 'lib/net/http/response.rb', line 181
def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only @socket = sock @body_exist = reqmethodallowbody && self.class.body_permitted? begin yield self.body # ensure to read body ensure @socket = nil end end
#response
header (for backward compatibility only; DO NOT USE)
# File 'lib/net/http/response.rb', line 162
def response #:nodoc: warn "Net::HTTPResponse#response is obsolete", uplevel: 1 if $VERBOSE self end
#scanning_meta(str) (private)
# File 'lib/net/http/response.rb', line 328
def (str) require 'strscan' ss = StringScanner.new(str) if ss.scan_until(/<meta[\t\n\f\r ]*/) attrs = {} # attribute_list got_pragma = false need_pragma = nil charset = nil # step: Attributes while attr = get_attribute(ss) name, value = *attr next if attrs[name] attrs[name] = true case name when 'http-equiv' got_pragma = true if value == 'content-type' when 'content' encoding = (value) unless charset charset = encoding end need_pragma = true when 'charset' need_pragma = false charset = value end end # step: Processing return if need_pragma.nil? return if need_pragma && !got_pragma charset = Encoding.find(charset) rescue nil return unless charset charset = Encoding::UTF_8 if charset == Encoding::UTF_16 return charset # tentative end nil end
#sniff_encoding(str, encoding = nil) (private)
# File 'lib/net/http/response.rb', line 298
def sniff_encoding(str, encoding=nil) # the encoding sniffing algorithm # http://www.w3.org/TR/html5/parsing.html#determining-the-character-encoding if enc = (str) enc # 6. last visited page or something # 7. frequency elsif str.ascii_only? Encoding::US_ASCII elsif str.dup.force_encoding(Encoding::UTF_8).valid_encoding? Encoding::UTF_8 end # 8. implementation-defined or user-specified end
#stream_check (private)
# File 'lib/net/http/response.rb', line 506
def stream_check raise IOError, 'attempt to read body out of block' if @socket.closed? end
#value
Raises an HTTP
error if the response is not 2xx (success).
# File 'lib/net/http/response.rb', line 150
def value error! unless self.kind_of?(Net::HTTPSuccess) end