зеркало из https://github.com/github/ruby.git
[rubygems/rubygems] Vendor net-http and net-protocol in RubyGems
https://github.com/rubygems/rubygems/commit/99d91c9ed2
This commit is contained in:
Родитель
6cefad7704
Коммит
ce924ce1fb
|
@ -84,7 +84,7 @@ module Gem::GemcutterUtilities
|
|||
# If +allowed_push_host+ metadata is present, then it will only allow that host.
|
||||
|
||||
def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, credentials: {}, &block)
|
||||
require "net/http"
|
||||
require_relative "net/http"
|
||||
|
||||
self.host = host if host
|
||||
unless self.host
|
||||
|
@ -119,7 +119,7 @@ module Gem::GemcutterUtilities
|
|||
end
|
||||
|
||||
def mfa_unauthorized?(response)
|
||||
response.is_a?(Net::HTTPUnauthorized) && response.body.start_with?("You have enabled multifactor authentication")
|
||||
response.is_a?(Gem::Net::HTTPUnauthorized) && response.body.start_with?("You have enabled multifactor authentication")
|
||||
end
|
||||
|
||||
def update_scope(scope)
|
||||
|
@ -208,13 +208,13 @@ module Gem::GemcutterUtilities
|
|||
|
||||
def with_response(response, error_prefix = nil)
|
||||
case response
|
||||
when Net::HTTPSuccess then
|
||||
when Gem::Net::HTTPSuccess then
|
||||
if block_given?
|
||||
yield response
|
||||
else
|
||||
say clean_text(response.body)
|
||||
end
|
||||
when Net::HTTPPermanentRedirect, Net::HTTPRedirection then
|
||||
when Gem::Net::HTTPPermanentRedirect, Gem::Net::HTTPRedirection then
|
||||
message = "The request has redirected permanently to #{response["location"]}. Please check your defined push host URL."
|
||||
message = "#{error_prefix}: #{message}" if error_prefix
|
||||
|
||||
|
@ -244,7 +244,7 @@ module Gem::GemcutterUtilities
|
|||
private
|
||||
|
||||
def request_with_otp(method, uri, &block)
|
||||
request_method = Net::HTTP.const_get method.to_s.capitalize
|
||||
request_method = Gem::Net::HTTP.const_get method.to_s.capitalize
|
||||
|
||||
Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
|
||||
req["OTP"] = otp if otp
|
||||
|
@ -297,7 +297,7 @@ module Gem::GemcutterUtilities
|
|||
request.basic_auth credentials[:email], credentials[:password]
|
||||
end
|
||||
end
|
||||
response.is_a?(Net::HTTPSuccess) ? response.body : nil
|
||||
response.is_a?(Gem::Net::HTTPSuccess) ? response.body : nil
|
||||
end
|
||||
|
||||
def pretty_host(host)
|
||||
|
@ -366,6 +366,6 @@ module Gem::GemcutterUtilities
|
|||
end
|
||||
|
||||
def api_key_forbidden?(response)
|
||||
response.is_a?(Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access")
|
||||
response.is_a?(Gem::Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
##
|
||||
# The WebauthnListener Response class is used by the WebauthnListener to create
|
||||
# responses to be sent to the Gem host. It creates a Net::HTTPResponse instance
|
||||
# responses to be sent to the Gem host. It creates a Gem::Net::HTTPResponse instance
|
||||
# when initialized and can be converted to the appropriate format to be sent by a socket using `to_s`.
|
||||
# Net::HTTPResponse instances cannot be directly sent over a socket.
|
||||
# Gem::Net::HTTPResponse instances cannot be directly sent over a socket.
|
||||
#
|
||||
# Types of response classes:
|
||||
# - OkResponse
|
||||
|
@ -60,7 +60,7 @@ module Gem::GemcutterUtilities
|
|||
def body; end
|
||||
|
||||
def build_http_response
|
||||
response_class = Net::HTTPResponse::CODE_TO_OBJ[code.to_s]
|
||||
response_class = Gem::Net::HTTPResponse::CODE_TO_OBJ[code.to_s]
|
||||
@http_response = response_class.new("1.1", code, reason_phrase)
|
||||
@http_response.instance_variable_set(:@read, true)
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ module Gem::GemcutterUtilities
|
|||
Timeout.timeout(TIMEOUT_IN_SECONDS) do
|
||||
loop do
|
||||
response = webauthn_verification_poll_response(webauthn_url, credentials)
|
||||
raise Gem::WebauthnVerificationError, response.message unless response.is_a?(Net::HTTPSuccess)
|
||||
raise Gem::WebauthnVerificationError, response.message unless response.is_a?(Gem::Net::HTTPSuccess)
|
||||
|
||||
require "json"
|
||||
parsed_response = JSON.parse(response.body)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
# for backward compatibility
|
||||
|
||||
# :enddoc:
|
||||
|
||||
class Gem::Net::HTTP
|
||||
ProxyMod = ProxyDelta
|
||||
deprecate_constant :ProxyMod
|
||||
end
|
||||
|
||||
module Gem::Net::NetPrivate
|
||||
HTTPRequest = ::Gem::Net::HTTPRequest
|
||||
deprecate_constant :HTTPRequest
|
||||
end
|
||||
|
||||
module Gem::Net
|
||||
HTTPSession = HTTP
|
||||
|
||||
HTTPInformationCode = HTTPInformation
|
||||
HTTPSuccessCode = HTTPSuccess
|
||||
HTTPRedirectionCode = HTTPRedirection
|
||||
HTTPRetriableCode = HTTPRedirection
|
||||
HTTPClientErrorCode = HTTPClientError
|
||||
HTTPFatalErrorCode = HTTPClientError
|
||||
HTTPServerErrorCode = HTTPServerError
|
||||
HTTPResponseReceiver = HTTPResponse
|
||||
|
||||
HTTPResponceReceiver = HTTPResponse # Typo since 2001
|
||||
|
||||
deprecate_constant :HTTPSession,
|
||||
:HTTPInformationCode,
|
||||
:HTTPSuccessCode,
|
||||
:HTTPRedirectionCode,
|
||||
:HTTPRetriableCode,
|
||||
:HTTPClientErrorCode,
|
||||
:HTTPFatalErrorCode,
|
||||
:HTTPServerErrorCode,
|
||||
:HTTPResponseReceiver,
|
||||
:HTTPResponceReceiver
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
module Gem::Net
|
||||
# Gem::Net::HTTP exception class.
|
||||
# You cannot use Gem::Net::HTTPExceptions directly; instead, you must use
|
||||
# its subclasses.
|
||||
module HTTPExceptions
|
||||
def initialize(msg, res) #:nodoc:
|
||||
super msg
|
||||
@response = res
|
||||
end
|
||||
attr_reader :response
|
||||
alias data response #:nodoc: obsolete
|
||||
end
|
||||
|
||||
class HTTPError < ProtocolError
|
||||
include HTTPExceptions
|
||||
end
|
||||
|
||||
class HTTPRetriableError < ProtoRetriableError
|
||||
include HTTPExceptions
|
||||
end
|
||||
|
||||
class HTTPClientException < ProtoServerError
|
||||
include HTTPExceptions
|
||||
end
|
||||
|
||||
class HTTPFatalError < ProtoFatalError
|
||||
include HTTPExceptions
|
||||
end
|
||||
|
||||
# We cannot use the name "HTTPServerError", it is the name of the response.
|
||||
HTTPServerException = HTTPClientException # :nodoc:
|
||||
deprecate_constant(:HTTPServerException)
|
||||
end
|
|
@ -0,0 +1,414 @@
|
|||
# frozen_string_literal: true
|
||||
#
|
||||
# \HTTPGenericRequest is the parent of the Gem::Net::HTTPRequest class.
|
||||
#
|
||||
# Do not use this directly; instead, use a subclass of Gem::Net::HTTPRequest.
|
||||
#
|
||||
# == About the Examples
|
||||
#
|
||||
# :include: doc/net-http/examples.rdoc
|
||||
#
|
||||
class Gem::Net::HTTPGenericRequest
|
||||
|
||||
include Gem::Net::HTTPHeader
|
||||
|
||||
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 Gem::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
|
||||
|
||||
# Returns the string method name for the request:
|
||||
#
|
||||
# Gem::Net::HTTP::Get.new(uri).method # => "GET"
|
||||
# Gem::Net::HTTP::Post.new(uri).method # => "POST"
|
||||
#
|
||||
attr_reader :method
|
||||
|
||||
# Returns the string path for the request:
|
||||
#
|
||||
# Gem::Net::HTTP::Get.new(uri).path # => "/"
|
||||
# Gem::Net::HTTP::Post.new('example.com').path # => "example.com"
|
||||
#
|
||||
attr_reader :path
|
||||
|
||||
# Returns the URI object for the request, or +nil+ if none:
|
||||
#
|
||||
# Gem::Net::HTTP::Get.new(uri).uri
|
||||
# # => #<URI::HTTPS https://jsonplaceholder.typicode.com/>
|
||||
# Gem::Net::HTTP::Get.new('example.com').uri # => nil
|
||||
#
|
||||
attr_reader :uri
|
||||
|
||||
# Returns +false+ if the request's header <tt>'Accept-Encoding'</tt>
|
||||
# has been set manually or deleted
|
||||
# (indicating that the user intends to handle encoding in the response),
|
||||
# +true+ otherwise:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri) # => #<Gem::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
|
||||
#
|
||||
attr_reader :decode_content
|
||||
|
||||
# Returns a string representation of the request:
|
||||
#
|
||||
# Gem::Net::HTTP::Post.new(uri).inspect # => "#<Gem::Net::HTTP::Post POST>"
|
||||
#
|
||||
def inspect
|
||||
"\#<#{self.class} #{@method}>"
|
||||
end
|
||||
|
||||
##
|
||||
# Don't automatically decode response content-encoding if the user indicates
|
||||
# they want to handle it.
|
||||
|
||||
def []=(key, val) # :nodoc:
|
||||
@decode_content = false if key.downcase == 'accept-encoding'
|
||||
|
||||
super key, val
|
||||
end
|
||||
|
||||
# Returns whether the request may have a body:
|
||||
#
|
||||
# Gem::Net::HTTP::Post.new(uri).request_body_permitted? # => true
|
||||
# Gem::Net::HTTP::Get.new(uri).request_body_permitted? # => false
|
||||
#
|
||||
def request_body_permitted?
|
||||
@request_has_body
|
||||
end
|
||||
|
||||
# Returns whether the response may have a body:
|
||||
#
|
||||
# Gem::Net::HTTP::Post.new(uri).response_body_permitted? # => true
|
||||
# Gem::Net::HTTP::Head.new(uri).response_body_permitted? # => false
|
||||
#
|
||||
def response_body_permitted?
|
||||
@response_has_body
|
||||
end
|
||||
|
||||
def body_exist? # :nodoc:
|
||||
warn "Gem::Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?", uplevel: 1 if $VERBOSE
|
||||
response_body_permitted?
|
||||
end
|
||||
|
||||
# Returns the string body for the request, or +nil+ if there is none:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Post.new(uri)
|
||||
# req.body # => nil
|
||||
# req.body = '{"title": "foo","body": "bar","userId": 1}'
|
||||
# req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
|
||||
#
|
||||
attr_reader :body
|
||||
|
||||
# Sets the body for the request:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Post.new(uri)
|
||||
# req.body # => nil
|
||||
# req.body = '{"title": "foo","body": "bar","userId": 1}'
|
||||
# req.body # => "{\"title\": \"foo\",\"body\": \"bar\",\"userId\": 1}"
|
||||
#
|
||||
def body=(str)
|
||||
@body = str
|
||||
@body_stream = nil
|
||||
@body_data = nil
|
||||
str
|
||||
end
|
||||
|
||||
# Returns the body stream object for the request, or +nil+ if there is none:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
|
||||
# req.body_stream # => nil
|
||||
# require 'stringio'
|
||||
# req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
|
||||
# req.body_stream # => #<StringIO:0x0000027d1e5affa8>
|
||||
#
|
||||
attr_reader :body_stream
|
||||
|
||||
# Sets the body stream for the request:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
|
||||
# req.body_stream # => nil
|
||||
# require 'stringio'
|
||||
# req.body_stream = StringIO.new('xyzzy') # => #<StringIO:0x0000027d1e5affa8>
|
||||
# req.body_stream # => #<StringIO:0x0000027d1e5affa8>
|
||||
#
|
||||
def body_stream=(input)
|
||||
@body = nil
|
||||
@body_stream = input
|
||||
@body_data = nil
|
||||
input
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
#
|
||||
# write
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
class Chunker #:nodoc:
|
||||
def initialize(sock)
|
||||
@sock = sock
|
||||
@prev = nil
|
||||
end
|
||||
|
||||
def write(buf)
|
||||
# avoid memcpy() of buf, buf can huge and eat memory bandwidth
|
||||
rv = buf.bytesize
|
||||
@sock.write("#{rv.to_s(16)}\r\n", buf, "\r\n")
|
||||
rv
|
||||
end
|
||||
|
||||
def finish
|
||||
@sock.write("0\r\n\r\n")
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
def quote_string(str, charset)
|
||||
str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset
|
||||
str.gsub(/[\\"]/, '\\\\\&')
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
##
|
||||
# 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.
|
||||
|
||||
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 = Gem::Net::HTTPResponse.read_new(sock)
|
||||
unless res.kind_of?(Gem::Net::HTTPContinue)
|
||||
res.decode_content = @decode_content
|
||||
throw :response, res
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,981 @@
|
|||
# frozen_string_literal: true
|
||||
#
|
||||
# The \HTTPHeader module provides access to \HTTP headers.
|
||||
#
|
||||
# The module is included in:
|
||||
#
|
||||
# - Gem::Net::HTTPGenericRequest (and therefore Gem::Net::HTTPRequest).
|
||||
# - Gem::Net::HTTPResponse.
|
||||
#
|
||||
# The headers are a hash-like collection of key/value pairs called _fields_.
|
||||
#
|
||||
# == Request and Response Fields
|
||||
#
|
||||
# Headers may be included in:
|
||||
#
|
||||
# - A Gem::Net::HTTPRequest object:
|
||||
# the object's headers will be sent with the request.
|
||||
# Any fields may be defined in the request;
|
||||
# see {Setters}[rdoc-ref:Gem::Net::HTTPHeader@Setters].
|
||||
# - A Gem::Net::HTTPResponse object:
|
||||
# the objects headers are usually those returned from the host.
|
||||
# Fields may be retrieved from the object;
|
||||
# see {Getters}[rdoc-ref:Gem::Net::HTTPHeader@Getters]
|
||||
# and {Iterators}[rdoc-ref:Gem::Net::HTTPHeader@Iterators].
|
||||
#
|
||||
# Exactly which fields should be sent or expected depends on the host;
|
||||
# see:
|
||||
#
|
||||
# - {Request fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields].
|
||||
# - {Response fields}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Response_fields].
|
||||
#
|
||||
# == 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 <tt>'Accept'</tt> is treated as if it were
|
||||
# <tt>'Accept'.downcase</tt>; i.e., <tt>'accept'</tt>.
|
||||
# - A symbol: Key <tt>:Accept</tt> is treated as if it were
|
||||
# <tt>:Accept.to_s.downcase</tt>; i.e., <tt>'accept'</tt>.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# req = Gem::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
|
||||
# <tt>self[key.downcase.to_s].join(', '))</tt>:
|
||||
#
|
||||
# - #[]: 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.
|
||||
#
|
||||
# === Setters
|
||||
#
|
||||
# \Method #[]= can set any field, but does little to validate the new value;
|
||||
# some of the other setter methods provide some validation:
|
||||
#
|
||||
# - #[]=: Sets the string or array value for the given key.
|
||||
# - #add_field: Creates or adds to the array value for the given key.
|
||||
# - #basic_auth: Sets the string authorization header for <tt>'Authorization'</tt>.
|
||||
# - #content_length=: Sets the integer length for field <tt>'Content-Length</tt>.
|
||||
# - #content_type=: Sets the string value for field <tt>'Content-Type'</tt>.
|
||||
# - #proxy_basic_auth: Sets the string authorization header for <tt>'Proxy-Authorization'</tt>.
|
||||
# - #set_range: Sets the value for field <tt>'Range'</tt>.
|
||||
#
|
||||
# === Form Setters
|
||||
#
|
||||
# - #set_form: Sets an HTML form data set.
|
||||
# - #set_form_data: Sets header fields and a body from HTML form data.
|
||||
#
|
||||
# === Getters
|
||||
#
|
||||
# \Method #[] can retrieve the value of any field that exists,
|
||||
# but always as a string;
|
||||
# some of the other getter methods return something different
|
||||
# from the simple string value:
|
||||
#
|
||||
# - #[]: Returns the string field value for the given key.
|
||||
# - #content_length: Returns the integer value of field <tt>'Content-Length'</tt>.
|
||||
# - #content_range: Returns the Range value of field <tt>'Content-Range'</tt>.
|
||||
# - #content_type: Returns the string value of field <tt>'Content-Type'</tt>.
|
||||
# - #fetch: Returns the string field value for the given key.
|
||||
# - #get_fields: Returns the array field value for the given +key+.
|
||||
# - #main_type: Returns first part of the string value of field <tt>'Content-Type'</tt>.
|
||||
# - #sub_type: Returns second part of the string value of field <tt>'Content-Type'</tt>.
|
||||
# - #range: Returns an array of Range objects of field <tt>'Range'</tt>, or +nil+.
|
||||
# - #range_length: Returns the integer length of the range given in field <tt>'Content-Range'</tt>.
|
||||
# - #type_params: Returns the string parameters for <tt>'Content-Type'</tt>.
|
||||
#
|
||||
# === Queries
|
||||
#
|
||||
# - #chunked?: Returns whether field <tt>'Transfer-Encoding'</tt> is set to <tt>'chunked'</tt>.
|
||||
# - #connection_close?: Returns whether field <tt>'Connection'</tt> is set to <tt>'close'</tt>.
|
||||
# - #connection_keep_alive?: Returns whether field <tt>'Connection'</tt> is set to <tt>'keep-alive'</tt>.
|
||||
# - #key?: Returns whether a given key exists.
|
||||
#
|
||||
# === 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 string field value to the block.
|
||||
#
|
||||
module Gem::Net::HTTPHeader
|
||||
MAX_KEY_LENGTH = 1024
|
||||
MAX_FIELD_LENGTH = 65536
|
||||
|
||||
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 size #:nodoc: obsolete
|
||||
@header.size
|
||||
end
|
||||
|
||||
alias length size #:nodoc: obsolete
|
||||
|
||||
# Returns the string field value for the case-insensitive field +key+,
|
||||
# or +nil+ if there is no such key;
|
||||
# see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['Connection'] # => "keep-alive"
|
||||
# res['Nosuch'] # => nil
|
||||
#
|
||||
# Note that some field values may be retrieved via convenience methods;
|
||||
# see {Getters}[rdoc-ref:Gem::Net::HTTPHeader@Getters].
|
||||
def [](key)
|
||||
a = @header[key.downcase.to_s] or return nil
|
||||
a.join(', ')
|
||||
end
|
||||
|
||||
# Sets the value for the case-insensitive +key+ to +val+,
|
||||
# overwriting the previous value if the field exists;
|
||||
# see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req['Accept'] # => "*/*"
|
||||
# req['Accept'] = 'text/html'
|
||||
# req['Accept'] # => "text/html"
|
||||
#
|
||||
# Note that some field values may be set via convenience methods;
|
||||
# see {Setters}[rdoc-ref:Gem::Net::HTTPHeader@Setters].
|
||||
def []=(key, val)
|
||||
unless val
|
||||
@header.delete key.downcase.to_s
|
||||
return val
|
||||
end
|
||||
set_field(key, val)
|
||||
end
|
||||
|
||||
# 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:Gem::Net::HTTPHeader@Fields]:
|
||||
#
|
||||
# req = Gem::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
|
||||
if @header.key?(stringified_downcased_key)
|
||||
append_field_value(@header[stringified_downcased_key], val)
|
||||
else
|
||||
set_field(key, val)
|
||||
end
|
||||
end
|
||||
|
||||
private def set_field(key, val)
|
||||
case val
|
||||
when Enumerable
|
||||
ary = []
|
||||
append_field_value(ary, val)
|
||||
@header[key.downcase.to_s] = ary
|
||||
else
|
||||
val = val.to_s # for compatibility use to_s instead of to_str
|
||||
if val.b.count("\r\n") > 0
|
||||
raise ArgumentError, 'header field value cannot include CR/LF'
|
||||
end
|
||||
@header[key.downcase.to_s] = [val]
|
||||
end
|
||||
end
|
||||
|
||||
private def append_field_value(ary, val)
|
||||
case val
|
||||
when Enumerable
|
||||
val.each{|x| append_field_value(ary, x)}
|
||||
else
|
||||
val = val.to_s
|
||||
if /[\r\n]/n.match?(val.b)
|
||||
raise ArgumentError, 'header field value cannot include CR/LF'
|
||||
end
|
||||
ary.push val
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the array field value for the given +key+,
|
||||
# or +nil+ if there is no such field;
|
||||
# see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res.get_fields('Connection') # => ["keep-alive"]
|
||||
# res.get_fields('Nosuch') # => nil
|
||||
#
|
||||
def get_fields(key)
|
||||
stringified_downcased_key = key.downcase.to_s
|
||||
return nil unless @header[stringified_downcased_key]
|
||||
@header[stringified_downcased_key].dup
|
||||
end
|
||||
|
||||
# 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:Gem::Net::HTTPHeader@Fields]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
#
|
||||
# # Field exists; block not called.
|
||||
# res.fetch('Connection') do |value|
|
||||
# fail 'Cannot happen'
|
||||
# end # => "keep-alive"
|
||||
#
|
||||
# # Field does not exist; block called.
|
||||
# res.fetch('Nosuch') do |value|
|
||||
# value.downcase
|
||||
# end # => "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:
|
||||
#
|
||||
# res.fetch('Connection', 'Foo') # => "keep-alive"
|
||||
# res.fetch('Nosuch', 'Foo') # => "Foo"
|
||||
# res.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
|
||||
|
||||
# Calls the block with each key/value pair:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res.each_header do |key, value|
|
||||
# p [key, value] if key.start_with?('c')
|
||||
# end
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# ["content-type", "application/json; charset=utf-8"]
|
||||
# ["connection", "keep-alive"]
|
||||
# ["cache-control", "max-age=43200"]
|
||||
# ["cf-cache-status", "HIT"]
|
||||
# ["cf-ray", "771d17e9bc542cf5-ORD"]
|
||||
#
|
||||
# Returns an enumerator if no block is given.
|
||||
#
|
||||
# Gem::Net::HTTPHeader#each is an alias for Gem::Net::HTTPHeader#each_header.
|
||||
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
|
||||
|
||||
alias each each_header
|
||||
|
||||
# Calls the block with each field key:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res.each_key do |key|
|
||||
# p key if key.start_with?('c')
|
||||
# end
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# "content-type"
|
||||
# "connection"
|
||||
# "cache-control"
|
||||
# "cf-cache-status"
|
||||
# "cf-ray"
|
||||
#
|
||||
# Returns an enumerator if no block is given.
|
||||
#
|
||||
# Gem::Net::HTTPHeader#each_name is an alias for Gem::Net::HTTPHeader#each_key.
|
||||
def each_name(&block) #:yield: +key+
|
||||
block_given? or return enum_for(__method__) { @header.size }
|
||||
@header.each_key(&block)
|
||||
end
|
||||
|
||||
alias each_key each_name
|
||||
|
||||
# Calls the block with each capitalized field name:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res.each_capitalized_name do |key|
|
||||
# p key if key.start_with?('C')
|
||||
# end
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# "Content-Type"
|
||||
# "Connection"
|
||||
# "Cache-Control"
|
||||
# "Cf-Cache-Status"
|
||||
# "Cf-Ray"
|
||||
#
|
||||
# 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+
|
||||
block_given? or return enum_for(__method__) { @header.size }
|
||||
@header.each_key do |k|
|
||||
yield capitalize(k)
|
||||
end
|
||||
end
|
||||
|
||||
# Calls the block with each string field value:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res.each_value do |value|
|
||||
# p value if value.start_with?('c')
|
||||
# end
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# "chunked"
|
||||
# "cf-q-config;dur=6.0000002122251e-06"
|
||||
# "cloudflare"
|
||||
#
|
||||
# Returns an enumerator if no block is given.
|
||||
def each_value #:yield: +value+
|
||||
block_given? or return enum_for(__method__) { @header.size }
|
||||
@header.each_value do |va|
|
||||
yield va.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
# Removes the header for the given case-insensitive +key+
|
||||
# (see {Fields}[rdoc-ref:Gem::Net::HTTPHeader@Fields]);
|
||||
# returns the deleted value, or +nil+ if no such field exists:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req.delete('Accept') # => ["*/*"]
|
||||
# req.delete('Nosuch') # => nil
|
||||
#
|
||||
def delete(key)
|
||||
@header.delete(key.downcase.to_s)
|
||||
end
|
||||
|
||||
# Returns +true+ if the field for the case-insensitive +key+ exists, +false+ otherwise:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req.key?('Accept') # => true
|
||||
# req.key?('Nosuch') # => false
|
||||
#
|
||||
def key?(key)
|
||||
@header.key?(key.downcase.to_s)
|
||||
end
|
||||
|
||||
# Returns a hash of the key/value pairs:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req.to_hash
|
||||
# # =>
|
||||
# {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
|
||||
# "accept"=>["*/*"],
|
||||
# "user-agent"=>["Ruby"],
|
||||
# "host"=>["jsonplaceholder.typicode.com"]}
|
||||
#
|
||||
def to_hash
|
||||
@header.dup
|
||||
end
|
||||
|
||||
# Like #each_header, but the keys are returned in capitalized form.
|
||||
#
|
||||
# Gem::Net::HTTPHeader#canonical_each is an alias for Gem::Net::HTTPHeader#each_capitalized.
|
||||
def each_capitalized
|
||||
block_given? or return enum_for(__method__) { @header.size }
|
||||
@header.each do |k,v|
|
||||
yield capitalize(k), v.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
alias canonical_each each_capitalized
|
||||
|
||||
def capitalize(name)
|
||||
name.to_s.split(/-/).map {|s| s.capitalize }.join('-')
|
||||
end
|
||||
private :capitalize
|
||||
|
||||
# Returns an array of Range objects that represent
|
||||
# the value of field <tt>'Range'</tt>,
|
||||
# or +nil+ if there is no such field;
|
||||
# see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req['Range'] = 'bytes=0-99,200-299,400-499'
|
||||
# req.range # => [0..99, 200..299, 400..499]
|
||||
# req.delete('Range')
|
||||
# req.range # # => nil
|
||||
#
|
||||
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
|
||||
|
||||
# call-seq:
|
||||
# set_range(length) -> length
|
||||
# set_range(offset, length) -> range
|
||||
# set_range(begin..length) -> range
|
||||
#
|
||||
# Sets the value for field <tt>'Range'</tt>;
|
||||
# see {Range request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#range-request-header]:
|
||||
#
|
||||
# With argument +length+:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req.set_range(100) # => 100
|
||||
# req['Range'] # => "bytes=0-99"
|
||||
#
|
||||
# With arguments +offset+ and +length+:
|
||||
#
|
||||
# req.set_range(100, 100) # => 100...200
|
||||
# req['Range'] # => "bytes=100-199"
|
||||
#
|
||||
# With argument +range+:
|
||||
#
|
||||
# req.set_range(100..199) # => 100..199
|
||||
# req['Range'] # => "bytes=100-199"
|
||||
#
|
||||
# Gem::Net::HTTPHeader#range= is an alias for Gem::Net::HTTPHeader#set_range.
|
||||
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
|
||||
|
||||
alias range= set_range
|
||||
|
||||
# Returns the value of field <tt>'Content-Length'</tt> as an integer,
|
||||
# or +nil+ if there is no such field;
|
||||
# see {Content-Length request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-request-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/nosuch/1')
|
||||
# res.content_length # => 2
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res.content_length # => nil
|
||||
#
|
||||
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
|
||||
|
||||
# Sets the value of field <tt>'Content-Length'</tt> to the given numeric;
|
||||
# see {Content-Length response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-length-response-header]:
|
||||
#
|
||||
# _uri = uri.dup
|
||||
# hostname = _uri.hostname # => "jsonplaceholder.typicode.com"
|
||||
# _uri.path = '/posts' # => "/posts"
|
||||
# req = Gem::Net::HTTP::Post.new(_uri) # => #<Gem::Net::HTTP::Post POST>
|
||||
# req.body = '{"title": "foo","body": "bar","userId": 1}'
|
||||
# req.content_length = req.body.size # => 42
|
||||
# req.content_type = 'application/json'
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end # => #<Gem::Net::HTTPCreated 201 Created readbody=true>
|
||||
#
|
||||
def content_length=(len)
|
||||
unless len
|
||||
@header.delete 'content-length'
|
||||
return nil
|
||||
end
|
||||
@header['content-length'] = [len.to_i.to_s]
|
||||
end
|
||||
|
||||
# Returns +true+ if field <tt>'Transfer-Encoding'</tt>
|
||||
# exists and has value <tt>'chunked'</tt>,
|
||||
# +false+ otherwise;
|
||||
# see {Transfer-Encoding response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#transfer-encoding-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['Transfer-Encoding'] # => "chunked"
|
||||
# res.chunked? # => true
|
||||
#
|
||||
def chunked?
|
||||
return false unless @header['transfer-encoding']
|
||||
field = self['Transfer-Encoding']
|
||||
(/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false
|
||||
end
|
||||
|
||||
# Returns a Range object representing the value of field
|
||||
# <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
|
||||
# see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['Content-Range'] # => nil
|
||||
# res['Content-Range'] = 'bytes 0-499/1000'
|
||||
# res['Content-Range'] # => "bytes 0-499/1000"
|
||||
# res.content_range # => 0..499
|
||||
#
|
||||
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
|
||||
|
||||
# Returns the integer representing length of the value of field
|
||||
# <tt>'Content-Range'</tt>, or +nil+ if no such field exists;
|
||||
# see {Content-Range response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-range-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['Content-Range'] # => nil
|
||||
# res['Content-Range'] = 'bytes 0-499/1000'
|
||||
# res.range_length # => 500
|
||||
#
|
||||
def range_length
|
||||
r = content_range() or return nil
|
||||
r.end - r.begin + 1
|
||||
end
|
||||
|
||||
# Returns the {media type}[https://en.wikipedia.org/wiki/Media_type]
|
||||
# from the value of field <tt>'Content-Type'</tt>,
|
||||
# or +nil+ if no such field exists;
|
||||
# see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['content-type'] # => "application/json; charset=utf-8"
|
||||
# res.content_type # => "application/json"
|
||||
#
|
||||
def content_type
|
||||
main = main_type()
|
||||
return nil unless main
|
||||
|
||||
sub = sub_type()
|
||||
if sub
|
||||
"#{main}/#{sub}"
|
||||
else
|
||||
main
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the leading ('type') part of the
|
||||
# {media type}[https://en.wikipedia.org/wiki/Media_type]
|
||||
# from the value of field <tt>'Content-Type'</tt>,
|
||||
# or +nil+ if no such field exists;
|
||||
# see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['content-type'] # => "application/json; charset=utf-8"
|
||||
# res.main_type # => "application"
|
||||
#
|
||||
def main_type
|
||||
return nil unless @header['content-type']
|
||||
self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
|
||||
end
|
||||
|
||||
# Returns the trailing ('subtype') part of the
|
||||
# {media type}[https://en.wikipedia.org/wiki/Media_type]
|
||||
# from the value of field <tt>'Content-Type'</tt>,
|
||||
# or +nil+ if no such field exists;
|
||||
# see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['content-type'] # => "application/json; charset=utf-8"
|
||||
# res.sub_type # => "json"
|
||||
#
|
||||
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
|
||||
|
||||
# Returns the trailing ('parameters') part of the value of field <tt>'Content-Type'</tt>,
|
||||
# or +nil+ if no such field exists;
|
||||
# see {Content-Type response header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-response-header]:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(hostname, '/todos/1')
|
||||
# res['content-type'] # => "application/json; charset=utf-8"
|
||||
# res.type_params # => {"charset"=>"utf-8"}
|
||||
#
|
||||
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
|
||||
|
||||
# Sets the value of field <tt>'Content-Type'</tt>;
|
||||
# returns the new value;
|
||||
# see {Content-Type request header}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-type-request-header]:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# req.set_content_type('application/json') # => ["application/json"]
|
||||
#
|
||||
# Gem::Net::HTTPHeader#content_type= is an alias for Gem::Net::HTTPHeader#set_content_type.
|
||||
def set_content_type(type, params = {})
|
||||
@header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')]
|
||||
end
|
||||
|
||||
alias content_type= set_content_type
|
||||
|
||||
# Sets the request body to a URL-encoded string derived from argument +params+,
|
||||
# and sets request header field <tt>'Content-Type'</tt>
|
||||
# to <tt>'application/x-www-form-urlencoded'</tt>.
|
||||
#
|
||||
# The resulting request is suitable for HTTP request +POST+ or +PUT+.
|
||||
#
|
||||
# Argument +params+ must be suitable for use as argument +enum+ to
|
||||
# {URI.encode_www_form}[https://docs.ruby-lang.org/en/master/URI.html#method-c-encode_www_form].
|
||||
#
|
||||
# With only argument +params+ given,
|
||||
# sets the body to a URL-encoded string with the default separator <tt>'&'</tt>:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Post.new('example.com')
|
||||
#
|
||||
# req.set_form_data(q: 'ruby', lang: 'en')
|
||||
# req.body # => "q=ruby&lang=en"
|
||||
# req['Content-Type'] # => "application/x-www-form-urlencoded"
|
||||
#
|
||||
# req.set_form_data([['q', 'ruby'], ['lang', 'en']])
|
||||
# req.body # => "q=ruby&lang=en"
|
||||
#
|
||||
# req.set_form_data(q: ['ruby', 'perl'], lang: 'en')
|
||||
# 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"
|
||||
#
|
||||
# With string argument +sep+ also given,
|
||||
# uses that string as the separator:
|
||||
#
|
||||
# req.set_form_data({q: 'ruby', lang: 'en'}, '|')
|
||||
# req.body # => "q=ruby|lang=en"
|
||||
#
|
||||
# Gem::Net::HTTPHeader#form_data= is an alias for Gem::Net::HTTPHeader#set_form_data.
|
||||
def set_form_data(params, sep = '&')
|
||||
query = URI.encode_www_form(params)
|
||||
query.gsub!(/&/, sep) if sep != '&'
|
||||
self.body = query
|
||||
self.content_type = 'application/x-www-form-urlencoded'
|
||||
end
|
||||
|
||||
alias form_data= set_form_data
|
||||
|
||||
# Stores form data to be used in a +POST+ or +PUT+ request.
|
||||
#
|
||||
# The form data given in +params+ consists of zero or more fields;
|
||||
# each field is:
|
||||
#
|
||||
# - A scalar value.
|
||||
# - A name/value pair.
|
||||
# - An IO stream opened for reading.
|
||||
#
|
||||
# Argument +params+ should be an
|
||||
# {Enumerable}[https://docs.ruby-lang.org/en/master/Enumerable.html#module-Enumerable-label-Enumerable+in+Ruby+Classes]
|
||||
# (method <tt>params.map</tt> will be called),
|
||||
# and is often an array or hash.
|
||||
#
|
||||
# First, we set up a request:
|
||||
#
|
||||
# _uri = uri.dup
|
||||
# _uri.path ='/posts'
|
||||
# req = Gem::Net::HTTP::Post.new(_uri)
|
||||
#
|
||||
# <b>Argument +params+ As an Array</b>
|
||||
#
|
||||
# When +params+ is an array,
|
||||
# each of its elements is a subarray that defines a field;
|
||||
# the subarray may contain:
|
||||
#
|
||||
# - One string:
|
||||
#
|
||||
# req.set_form([['foo'], ['bar'], ['baz']])
|
||||
#
|
||||
# - Two strings:
|
||||
#
|
||||
# req.set_form([%w[foo 0], %w[bar 1], %w[baz 2]])
|
||||
#
|
||||
# - When argument +enctype+ (see below) is given as
|
||||
# <tt>'multipart/form-data'</tt>:
|
||||
#
|
||||
# - A string name and an IO stream opened for reading:
|
||||
#
|
||||
# require 'stringio'
|
||||
# req.set_form([['file', StringIO.new('Ruby is cool.')]])
|
||||
#
|
||||
# - A string name, an IO stream opened for reading,
|
||||
# and an options hash, which may contain these entries:
|
||||
#
|
||||
# - +:filename+: The name of the file to use.
|
||||
# - +:content_type+: The content type of the uploaded file.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# req.set_form([['file', file, {filename: "other-filename.foo"}]]
|
||||
#
|
||||
# The various forms may be mixed:
|
||||
#
|
||||
# req.set_form(['foo', %w[bar 1], ['file', file]])
|
||||
#
|
||||
# <b>Argument +params+ As a Hash</b>
|
||||
#
|
||||
# When +params+ is a hash,
|
||||
# each of its entries is a name/value pair that defines a field:
|
||||
#
|
||||
# - The name is a string.
|
||||
# - The value may be:
|
||||
#
|
||||
# - +nil+.
|
||||
# - Another string.
|
||||
# - An IO stream opened for reading
|
||||
# (only when argument +enctype+ -- see below -- is given as
|
||||
# <tt>'multipart/form-data'</tt>).
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Nil-valued fields.
|
||||
# req.set_form({'foo' => nil, 'bar' => nil, 'baz' => nil})
|
||||
#
|
||||
# # String-valued fields.
|
||||
# req.set_form({'foo' => 0, 'bar' => 1, 'baz' => 2})
|
||||
#
|
||||
# # IO-valued field.
|
||||
# require 'stringio'
|
||||
# req.set_form({'file' => StringIO.new('Ruby is cool.')})
|
||||
#
|
||||
# # Mixture of fields.
|
||||
# req.set_form({'foo' => nil, 'bar' => 1, 'file' => file})
|
||||
#
|
||||
# Optional argument +enctype+ specifies the value to be given
|
||||
# to field <tt>'Content-Type'</tt>, and must be one of:
|
||||
#
|
||||
# - <tt>'application/x-www-form-urlencoded'</tt> (the default).
|
||||
# - <tt>'multipart/form-data'</tt>;
|
||||
# see {RFC 7578}[https://www.rfc-editor.org/rfc/rfc7578].
|
||||
#
|
||||
# Optional argument +formopt+ is a hash of options
|
||||
# (applicable only when argument +enctype+
|
||||
# is <tt>'multipart/form-data'</tt>)
|
||||
# that may include the following entries:
|
||||
#
|
||||
# - +:boundary+: The value is the boundary string for the multipart message.
|
||||
# If not given, the boundary is a random string.
|
||||
# See {Boundary}[https://www.rfc-editor.org/rfc/rfc7578#section-4.1].
|
||||
# - +:charset+: Value is the character set for the form submission.
|
||||
# Field names and values of non-file fields should be encoded with this charset.
|
||||
#
|
||||
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
|
||||
|
||||
# Sets header <tt>'Authorization'</tt> using the given
|
||||
# +account+ and +password+ strings:
|
||||
#
|
||||
# req.basic_auth('my_account', 'my_password')
|
||||
# req['Authorization']
|
||||
# # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
|
||||
#
|
||||
def basic_auth(account, password)
|
||||
@header['authorization'] = [basic_encode(account, password)]
|
||||
end
|
||||
|
||||
# Sets header <tt>'Proxy-Authorization'</tt> using the given
|
||||
# +account+ and +password+ strings:
|
||||
#
|
||||
# req.proxy_basic_auth('my_account', 'my_password')
|
||||
# req['Proxy-Authorization']
|
||||
# # => "Basic bXlfYWNjb3VudDpteV9wYXNzd29yZA=="
|
||||
#
|
||||
def proxy_basic_auth(account, password)
|
||||
@header['proxy-authorization'] = [basic_encode(account, password)]
|
||||
end
|
||||
|
||||
def basic_encode(account, password)
|
||||
'Basic ' + ["#{account}:#{password}"].pack('m0')
|
||||
end
|
||||
private :basic_encode
|
||||
|
||||
# Returns whether the HTTP session is to be closed.
|
||||
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
|
||||
|
||||
# Returns whether the HTTP session is to be kept 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
|
||||
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
module Gem::Net::HTTP::ProxyDelta #:nodoc: internal use only
|
||||
private
|
||||
|
||||
def conn_address
|
||||
proxy_address()
|
||||
end
|
||||
|
||||
def conn_port
|
||||
proxy_port()
|
||||
end
|
||||
|
||||
def edit_path(path)
|
||||
use_ssl? ? path : "http://#{addr_port()}#{path}"
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This class is the base class for \Gem::Net::HTTP request classes.
|
||||
# The class should not be used directly;
|
||||
# instead you should use its subclasses, listed below.
|
||||
#
|
||||
# == Creating a Request
|
||||
#
|
||||
# An request object may be created with either a URI or a string hostname:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('https://jsonplaceholder.typicode.com/')
|
||||
# req = Gem::Net::HTTP::Get.new(uri) # => #<Gem::Net::HTTP::Get GET>
|
||||
# req = Gem::Net::HTTP::Get.new(uri.hostname) # => #<Gem::Net::HTTP::Get GET>
|
||||
#
|
||||
# And with any of the subclasses:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Head.new(uri) # => #<Gem::Net::HTTP::Head HEAD>
|
||||
# req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
|
||||
# req = Gem::Net::HTTP::Put.new(uri) # => #<Gem::Net::HTTP::Put PUT>
|
||||
# # ...
|
||||
#
|
||||
# The new instance is suitable for use as the argument to Gem::Net::HTTP#request.
|
||||
#
|
||||
# == Request Headers
|
||||
#
|
||||
# A new request object has these header fields by default:
|
||||
#
|
||||
# req.to_hash
|
||||
# # =>
|
||||
# {"accept-encoding"=>["gzip;q=1.0,deflate;q=0.6,identity;q=0.3"],
|
||||
# "accept"=>["*/*"],
|
||||
# "user-agent"=>["Ruby"],
|
||||
# "host"=>["jsonplaceholder.typicode.com"]}
|
||||
#
|
||||
# See:
|
||||
#
|
||||
# - {Request header Accept-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Accept-Encoding]
|
||||
# and {Compression and Decompression}[rdoc-ref:Gem::Net::HTTP@Compression+and+Decompression].
|
||||
# - {Request header Accept}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#accept-request-header].
|
||||
# - {Request header User-Agent}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#user-agent-request-header].
|
||||
# - {Request header Host}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#host-request-header].
|
||||
#
|
||||
# You can add headers or override default headers:
|
||||
#
|
||||
# # res = Gem::Net::HTTP::Get.new(uri, {'foo' => '0', 'bar' => '1'})
|
||||
#
|
||||
# This class (and therefore its subclasses) also includes (indirectly)
|
||||
# module Gem::Net::HTTPHeader, which gives access to its
|
||||
# {methods for setting headers}[rdoc-ref:Gem::Net::HTTPHeader@Setters].
|
||||
#
|
||||
# == Request Subclasses
|
||||
#
|
||||
# Subclasses for HTTP requests:
|
||||
#
|
||||
# - Gem::Net::HTTP::Get
|
||||
# - Gem::Net::HTTP::Head
|
||||
# - Gem::Net::HTTP::Post
|
||||
# - Gem::Net::HTTP::Put
|
||||
# - Gem::Net::HTTP::Delete
|
||||
# - Gem::Net::HTTP::Options
|
||||
# - Gem::Net::HTTP::Trace
|
||||
# - Gem::Net::HTTP::Patch
|
||||
#
|
||||
# Subclasses for WebDAV requests:
|
||||
#
|
||||
# - Gem::Net::HTTP::Propfind
|
||||
# - Gem::Net::HTTP::Proppatch
|
||||
# - Gem::Net::HTTP::Mkcol
|
||||
# - Gem::Net::HTTP::Copy
|
||||
# - Gem::Net::HTTP::Move
|
||||
# - Gem::Net::HTTP::Lock
|
||||
# - Gem::Net::HTTP::Unlock
|
||||
#
|
||||
class Gem::Net::HTTPRequest < Gem::Net::HTTPGenericRequest
|
||||
# Creates an HTTP request object for +path+.
|
||||
#
|
||||
# +initheader+ are the default headers to use. Gem::Net::HTTP adds
|
||||
# Accept-Encoding to enable compression of the response body unless
|
||||
# Accept-Encoding or Range are supplied in +initheader+.
|
||||
|
||||
def initialize(path, initheader = nil)
|
||||
super self.class::METHOD,
|
||||
self.class::REQUEST_HAS_BODY,
|
||||
self.class::RESPONSE_HAS_BODY,
|
||||
path, initheader
|
||||
end
|
||||
end
|
|
@ -0,0 +1,425 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# HTTP/1.1 methods --- RFC2616
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method GET}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#GET_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Get.new(uri) # => #<Gem::Net::HTTP::Get GET>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: optional.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP.get: sends +GET+ request, returns response body.
|
||||
# - Gem::Net::HTTP#get: sends +GET+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Get < Gem::Net::HTTPRequest
|
||||
METHOD = 'GET'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method HEAD}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#HEAD_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Head.new(uri) # => #<Gem::Net::HTTP::Head HEAD>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: optional.
|
||||
# - Response body: no.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#head: sends +HEAD+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Head < Gem::Net::HTTPRequest
|
||||
METHOD = 'HEAD'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = false
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method POST}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#POST_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# uri.path = '/posts'
|
||||
# req = Gem::Net::HTTP::Post.new(uri) # => #<Gem::Net::HTTP::Post POST>
|
||||
# req.body = '{"title": "foo","body": "bar","userId": 1}'
|
||||
# req.content_type = 'application/json'
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: yes.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: yes.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP.post: sends +POST+ request, returns response object.
|
||||
# - Gem::Net::HTTP#post: sends +POST+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Post < Gem::Net::HTTPRequest
|
||||
METHOD = 'POST'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method PUT}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PUT_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# uri.path = '/posts'
|
||||
# req = Gem::Net::HTTP::Put.new(uri) # => #<Gem::Net::HTTP::Put PUT>
|
||||
# req.body = '{"title": "foo","body": "bar","userId": 1}'
|
||||
# req.content_type = 'application/json'
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: yes.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
|
||||
#
|
||||
class Gem::Net::HTTP::Put < Gem::Net::HTTPRequest
|
||||
METHOD = 'PUT'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method DELETE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#DELETE_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# uri.path = '/posts/1'
|
||||
# req = Gem::Net::HTTP::Delete.new(uri) # => #<Gem::Net::HTTP::Delete DELETE>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: optional.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#delete: sends +DELETE+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Delete < Gem::Net::HTTPRequest
|
||||
METHOD = 'DELETE'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method OPTIONS}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#OPTIONS_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Options.new(uri) # => #<Gem::Net::HTTP::Options OPTIONS>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: optional.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#options: sends +OPTIONS+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Options < Gem::Net::HTTPRequest
|
||||
METHOD = 'OPTIONS'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method TRACE}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#TRACE_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Trace.new(uri) # => #<Gem::Net::HTTP::Trace TRACE>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: no.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: yes.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: yes.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#trace: sends +TRACE+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Trace < Gem::Net::HTTPRequest
|
||||
METHOD = 'TRACE'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {HTTP method PATCH}[https://en.wikipedia.org/w/index.php?title=Hypertext_Transfer_Protocol#PATCH_method]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# uri.path = '/posts'
|
||||
# req = Gem::Net::HTTP::Patch.new(uri) # => #<Gem::Net::HTTP::Patch PATCH>
|
||||
# req.body = '{"title": "foo","body": "bar","userId": 1}'
|
||||
# req.content_type = 'application/json'
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Properties:
|
||||
#
|
||||
# - Request body: yes.
|
||||
# - Response body: yes.
|
||||
# - {Safe}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods]: no.
|
||||
# - {Idempotent}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Idempotent_methods]: no.
|
||||
# - {Cacheable}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cacheable_methods]: no.
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#patch: sends +PATCH+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Patch < Gem::Net::HTTPRequest
|
||||
METHOD = 'PATCH'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
#
|
||||
# WebDAV methods --- RFC2518
|
||||
#
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method PROPFIND}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPFIND]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Propfind.new(uri) # => #<Gem::Net::HTTP::Propfind PROPFIND>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#propfind: sends +PROPFIND+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Propfind < Gem::Net::HTTPRequest
|
||||
METHOD = 'PROPFIND'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method PROPPATCH}[http://www.webdav.org/specs/rfc4918.html#METHOD_PROPPATCH]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Proppatch.new(uri) # => #<Gem::Net::HTTP::Proppatch PROPPATCH>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#proppatch: sends +PROPPATCH+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Proppatch < Gem::Net::HTTPRequest
|
||||
METHOD = 'PROPPATCH'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method MKCOL}[http://www.webdav.org/specs/rfc4918.html#METHOD_MKCOL]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Mkcol.new(uri) # => #<Gem::Net::HTTP::Mkcol MKCOL>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#mkcol: sends +MKCOL+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Mkcol < Gem::Net::HTTPRequest
|
||||
METHOD = 'MKCOL'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method COPY}[http://www.webdav.org/specs/rfc4918.html#METHOD_COPY]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Copy.new(uri) # => #<Gem::Net::HTTP::Copy COPY>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#copy: sends +COPY+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Copy < Gem::Net::HTTPRequest
|
||||
METHOD = 'COPY'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method MOVE}[http://www.webdav.org/specs/rfc4918.html#METHOD_MOVE]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Move.new(uri) # => #<Gem::Net::HTTP::Move MOVE>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#move: sends +MOVE+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Move < Gem::Net::HTTPRequest
|
||||
METHOD = 'MOVE'
|
||||
REQUEST_HAS_BODY = false
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method LOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_LOCK]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Lock.new(uri) # => #<Gem::Net::HTTP::Lock LOCK>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#lock: sends +LOCK+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Lock < Gem::Net::HTTPRequest
|
||||
METHOD = 'LOCK'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
||||
# \Class for representing
|
||||
# {WebDAV method UNLOCK}[http://www.webdav.org/specs/rfc4918.html#METHOD_UNLOCK]:
|
||||
#
|
||||
# require 'rubygems/net-http/lib/net/http'
|
||||
# uri = URI('http://example.com')
|
||||
# hostname = uri.hostname # => "example.com"
|
||||
# req = Gem::Net::HTTP::Unlock.new(uri) # => #<Gem::Net::HTTP::Unlock UNLOCK>
|
||||
# res = Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end
|
||||
#
|
||||
# See {Request Headers}[rdoc-ref:Gem::Net::HTTPRequest@Request+Headers].
|
||||
#
|
||||
# Related:
|
||||
#
|
||||
# - Gem::Net::HTTP#unlock: sends +UNLOCK+ request, returns response object.
|
||||
#
|
||||
class Gem::Net::HTTP::Unlock < Gem::Net::HTTPRequest
|
||||
METHOD = 'UNLOCK'
|
||||
REQUEST_HAS_BODY = true
|
||||
RESPONSE_HAS_BODY = true
|
||||
end
|
||||
|
|
@ -0,0 +1,738 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This class is the base class for \Gem::Net::HTTP response classes.
|
||||
#
|
||||
# == About the Examples
|
||||
#
|
||||
# :include: doc/net-http/examples.rdoc
|
||||
#
|
||||
# == Returned Responses
|
||||
#
|
||||
# \Method Gem::Net::HTTP.get_response returns
|
||||
# an instance of one of the subclasses of \Gem::Net::HTTPResponse:
|
||||
#
|
||||
# Gem::Net::HTTP.get_response(uri)
|
||||
# # => #<Gem::Net::HTTPOK 200 OK readbody=true>
|
||||
# Gem::Net::HTTP.get_response(hostname, '/nosuch')
|
||||
# # => #<Gem::Net::HTTPNotFound 404 Not Found readbody=true>
|
||||
#
|
||||
# As does method Gem::Net::HTTP#request:
|
||||
#
|
||||
# req = Gem::Net::HTTP::Get.new(uri)
|
||||
# Gem::Net::HTTP.start(hostname) do |http|
|
||||
# http.request(req)
|
||||
# end # => #<Gem::Net::HTTPOK 200 OK readbody=true>
|
||||
#
|
||||
# \Class \Gem::Net::HTTPResponse includes module Gem::Net::HTTPHeader,
|
||||
# which provides access to response header values via (among others):
|
||||
#
|
||||
# - \Hash-like method <tt>[]</tt>.
|
||||
# - Specific reader methods, such as +content_type+.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# res = Gem::Net::HTTP.get_response(uri) # => #<Gem::Net::HTTPOK 200 OK readbody=true>
|
||||
# res['Content-Type'] # => "text/html; charset=UTF-8"
|
||||
# res.content_type # => "text/html"
|
||||
#
|
||||
# == Response Subclasses
|
||||
#
|
||||
# \Class \Gem::Net::HTTPResponse has a subclass for each
|
||||
# {HTTP status code}[https://en.wikipedia.org/wiki/List_of_HTTP_status_codes].
|
||||
# You can look up the response class for a given code:
|
||||
#
|
||||
# Gem::Net::HTTPResponse::CODE_TO_OBJ['200'] # => Gem::Net::HTTPOK
|
||||
# Gem::Net::HTTPResponse::CODE_TO_OBJ['400'] # => Gem::Net::HTTPBadRequest
|
||||
# Gem::Net::HTTPResponse::CODE_TO_OBJ['404'] # => Gem::Net::HTTPNotFound
|
||||
#
|
||||
# And you can retrieve the status code for a response object:
|
||||
#
|
||||
# Gem::Net::HTTP.get_response(uri).code # => "200"
|
||||
# Gem::Net::HTTP.get_response(hostname, '/nosuch').code # => "404"
|
||||
#
|
||||
# The response subclasses (indentation shows class hierarchy):
|
||||
#
|
||||
# - Gem::Net::HTTPUnknownResponse (for unhandled \HTTP extensions).
|
||||
#
|
||||
# - Gem::Net::HTTPInformation:
|
||||
#
|
||||
# - Gem::Net::HTTPContinue (100)
|
||||
# - Gem::Net::HTTPSwitchProtocol (101)
|
||||
# - Gem::Net::HTTPProcessing (102)
|
||||
# - Gem::Net::HTTPEarlyHints (103)
|
||||
#
|
||||
# - Gem::Net::HTTPSuccess:
|
||||
#
|
||||
# - Gem::Net::HTTPOK (200)
|
||||
# - Gem::Net::HTTPCreated (201)
|
||||
# - Gem::Net::HTTPAccepted (202)
|
||||
# - Gem::Net::HTTPNonAuthoritativeInformation (203)
|
||||
# - Gem::Net::HTTPNoContent (204)
|
||||
# - Gem::Net::HTTPResetContent (205)
|
||||
# - Gem::Net::HTTPPartialContent (206)
|
||||
# - Gem::Net::HTTPMultiStatus (207)
|
||||
# - Gem::Net::HTTPAlreadyReported (208)
|
||||
# - Gem::Net::HTTPIMUsed (226)
|
||||
#
|
||||
# - Gem::Net::HTTPRedirection:
|
||||
#
|
||||
# - Gem::Net::HTTPMultipleChoices (300)
|
||||
# - Gem::Net::HTTPMovedPermanently (301)
|
||||
# - Gem::Net::HTTPFound (302)
|
||||
# - Gem::Net::HTTPSeeOther (303)
|
||||
# - Gem::Net::HTTPNotModified (304)
|
||||
# - Gem::Net::HTTPUseProxy (305)
|
||||
# - Gem::Net::HTTPTemporaryRedirect (307)
|
||||
# - Gem::Net::HTTPPermanentRedirect (308)
|
||||
#
|
||||
# - Gem::Net::HTTPClientError:
|
||||
#
|
||||
# - Gem::Net::HTTPBadRequest (400)
|
||||
# - Gem::Net::HTTPUnauthorized (401)
|
||||
# - Gem::Net::HTTPPaymentRequired (402)
|
||||
# - Gem::Net::HTTPForbidden (403)
|
||||
# - Gem::Net::HTTPNotFound (404)
|
||||
# - Gem::Net::HTTPMethodNotAllowed (405)
|
||||
# - Gem::Net::HTTPNotAcceptable (406)
|
||||
# - Gem::Net::HTTPProxyAuthenticationRequired (407)
|
||||
# - Gem::Net::HTTPRequestTimeOut (408)
|
||||
# - Gem::Net::HTTPConflict (409)
|
||||
# - Gem::Net::HTTPGone (410)
|
||||
# - Gem::Net::HTTPLengthRequired (411)
|
||||
# - Gem::Net::HTTPPreconditionFailed (412)
|
||||
# - Gem::Net::HTTPRequestEntityTooLarge (413)
|
||||
# - Gem::Net::HTTPRequestURITooLong (414)
|
||||
# - Gem::Net::HTTPUnsupportedMediaType (415)
|
||||
# - Gem::Net::HTTPRequestedRangeNotSatisfiable (416)
|
||||
# - Gem::Net::HTTPExpectationFailed (417)
|
||||
# - Gem::Net::HTTPMisdirectedRequest (421)
|
||||
# - Gem::Net::HTTPUnprocessableEntity (422)
|
||||
# - Gem::Net::HTTPLocked (423)
|
||||
# - Gem::Net::HTTPFailedDependency (424)
|
||||
# - Gem::Net::HTTPUpgradeRequired (426)
|
||||
# - Gem::Net::HTTPPreconditionRequired (428)
|
||||
# - Gem::Net::HTTPTooManyRequests (429)
|
||||
# - Gem::Net::HTTPRequestHeaderFieldsTooLarge (431)
|
||||
# - Gem::Net::HTTPUnavailableForLegalReasons (451)
|
||||
#
|
||||
# - Gem::Net::HTTPServerError:
|
||||
#
|
||||
# - Gem::Net::HTTPInternalServerError (500)
|
||||
# - Gem::Net::HTTPNotImplemented (501)
|
||||
# - Gem::Net::HTTPBadGateway (502)
|
||||
# - Gem::Net::HTTPServiceUnavailable (503)
|
||||
# - Gem::Net::HTTPGatewayTimeOut (504)
|
||||
# - Gem::Net::HTTPVersionNotSupported (505)
|
||||
# - Gem::Net::HTTPVariantAlsoNegotiates (506)
|
||||
# - Gem::Net::HTTPInsufficientStorage (507)
|
||||
# - Gem::Net::HTTPLoopDetected (508)
|
||||
# - Gem::Net::HTTPNotExtended (510)
|
||||
# - Gem::Net::HTTPNetworkAuthenticationRequired (511)
|
||||
#
|
||||
# There is also the Gem::Net::HTTPBadResponse exception which is raised when
|
||||
# there is a protocol error.
|
||||
#
|
||||
class Gem::Net::HTTPResponse
|
||||
class << self
|
||||
# true if the response has a body.
|
||||
def body_permitted?
|
||||
self::HAS_BODY
|
||||
end
|
||||
|
||||
def exception_type # :nodoc: internal use only
|
||||
self::EXCEPTION_TYPE
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
def read_status_line(sock)
|
||||
str = sock.readline
|
||||
m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?\z/in.match(str) or
|
||||
raise Gem::Net::HTTPBadResponse, "wrong status line: #{str.dump}"
|
||||
m.captures
|
||||
end
|
||||
|
||||
def response_class(code)
|
||||
CODE_TO_OBJ[code] or
|
||||
CODE_CLASS_TO_OBJ[code[0,1]] or
|
||||
Gem::Net::HTTPUnknownResponse
|
||||
end
|
||||
|
||||
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 Gem::Net::HTTPBadResponse, 'wrong header line format' if value.nil?
|
||||
end
|
||||
end
|
||||
yield key, value if key
|
||||
end
|
||||
end
|
||||
|
||||
# next is to fix bug in RDoc, where the private inside class << self
|
||||
# spills out.
|
||||
public
|
||||
|
||||
include Gem::Net::HTTPHeader
|
||||
|
||||
def initialize(httpv, code, msg) #:nodoc: internal use only
|
||||
@http_version = httpv
|
||||
@code = code
|
||||
@message = msg
|
||||
initialize_http_header nil
|
||||
@body = nil
|
||||
@read = false
|
||||
@uri = nil
|
||||
@decode_content = false
|
||||
@body_encoding = false
|
||||
@ignore_eof = true
|
||||
end
|
||||
|
||||
# The HTTP version supported by the server.
|
||||
attr_reader :http_version
|
||||
|
||||
# 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.
|
||||
attr_reader :code
|
||||
|
||||
# The HTTP result message sent by the server. For example, 'Not Found'.
|
||||
attr_reader :message
|
||||
alias msg message # :nodoc: obsolete
|
||||
|
||||
# The URI used to fetch this response. The response URI is only available
|
||||
# if a URI was used to create the request.
|
||||
attr_reader :uri
|
||||
|
||||
# Set to true automatically when the request did not contain an
|
||||
# Accept-Encoding header from the user.
|
||||
attr_accessor :decode_content
|
||||
|
||||
# Returns the value set by body_encoding=, or +false+ if none;
|
||||
# see #body_encoding=.
|
||||
attr_reader :body_encoding
|
||||
|
||||
# Sets the encoding that should be used when reading the body:
|
||||
#
|
||||
# - If the given value is an Encoding object, that encoding will be used.
|
||||
# - Otherwise if the value is a string, the value of
|
||||
# {Encoding#find(value)}[https://docs.ruby-lang.org/en/master/Encoding.html#method-c-find]
|
||||
# will be used.
|
||||
# - Otherwise an encoding will be deduced from the body itself.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# http = Gem::Net::HTTP.new(hostname)
|
||||
# req = Gem::Net::HTTP::Get.new('/')
|
||||
#
|
||||
# http.request(req) do |res|
|
||||
# p res.body.encoding # => #<Encoding:ASCII-8BIT>
|
||||
# end
|
||||
#
|
||||
# http.request(req) do |res|
|
||||
# res.body_encoding = "UTF-8"
|
||||
# p res.body.encoding # => #<Encoding:UTF-8>
|
||||
# end
|
||||
#
|
||||
def body_encoding=(value)
|
||||
value = Encoding.find(value) if value.is_a?(String)
|
||||
@body_encoding = value
|
||||
end
|
||||
|
||||
# Whether to ignore EOF when reading bodies with a specified Content-Length
|
||||
# header.
|
||||
attr_accessor :ignore_eof
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} #{@code} #{@message} readbody=#{@read}>"
|
||||
end
|
||||
|
||||
#
|
||||
# response <-> exception relationship
|
||||
#
|
||||
|
||||
def code_type #:nodoc:
|
||||
self.class
|
||||
end
|
||||
|
||||
def error! #:nodoc:
|
||||
message = @code
|
||||
message = "#{message} #{@message.dump}" if @message
|
||||
raise error_type().new(message, self)
|
||||
end
|
||||
|
||||
def error_type #:nodoc:
|
||||
self.class::EXCEPTION_TYPE
|
||||
end
|
||||
|
||||
# Raises an HTTP error if the response is not 2xx (success).
|
||||
def value
|
||||
error! unless self.kind_of?(Gem::Net::HTTPSuccess)
|
||||
end
|
||||
|
||||
def uri= uri # :nodoc:
|
||||
@uri = uri.dup if uri
|
||||
end
|
||||
|
||||
#
|
||||
# header (for backward compatibility only; DO NOT USE)
|
||||
#
|
||||
|
||||
def response #:nodoc:
|
||||
warn "Gem::Net::HTTPResponse#response is obsolete", uplevel: 1 if $VERBOSE
|
||||
self
|
||||
end
|
||||
|
||||
def header #:nodoc:
|
||||
warn "Gem::Net::HTTPResponse#header is obsolete", uplevel: 1 if $VERBOSE
|
||||
self
|
||||
end
|
||||
|
||||
def read_header #:nodoc:
|
||||
warn "Gem::Net::HTTPResponse#read_header is obsolete", uplevel: 1 if $VERBOSE
|
||||
self
|
||||
end
|
||||
|
||||
#
|
||||
# body
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
# 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 <code>dest#<<</code> method (it could be String or IO, or any
|
||||
# other object responding to <code><<</code>).
|
||||
#
|
||||
# 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
|
||||
# }
|
||||
#
|
||||
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
|
||||
return if @body.nil?
|
||||
|
||||
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
|
||||
|
||||
# Returns the string response body;
|
||||
# note that repeated calls for the unmodified body return a cached string:
|
||||
#
|
||||
# path = '/todos/1'
|
||||
# Gem::Net::HTTP.start(hostname) do |http|
|
||||
# res = http.get(path)
|
||||
# p res.body
|
||||
# p http.head(path).body # No body.
|
||||
# end
|
||||
#
|
||||
# Output:
|
||||
#
|
||||
# "{\n \"userId\": 1,\n \"id\": 1,\n \"title\": \"delectus aut autem\",\n \"completed\": false\n}"
|
||||
# nil
|
||||
#
|
||||
def body
|
||||
read_body()
|
||||
end
|
||||
|
||||
# Sets the body of the response to the given value.
|
||||
def body=(value)
|
||||
@body = value
|
||||
end
|
||||
|
||||
alias entity body #:nodoc: obsolete
|
||||
|
||||
private
|
||||
|
||||
# :nodoc:
|
||||
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
|
||||
|
||||
# :nodoc:
|
||||
def sniff_encoding(str, encoding=nil)
|
||||
# the encoding sniffing algorithm
|
||||
# http://www.w3.org/TR/html5/parsing.html#determining-the-character-encoding
|
||||
if enc = scanning_meta(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
|
||||
|
||||
# :nodoc:
|
||||
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
|
||||
|
||||
# :nodoc:
|
||||
def scanning_meta(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 = extracting_encodings_from_meta_elements(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
|
||||
|
||||
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
|
||||
|
||||
def extracting_encodings_from_meta_elements(value)
|
||||
# http://dev.w3.org/html5/spec/fetching-resources.html#algorithm-for-extracting-an-encoding-from-a-meta-element
|
||||
if /charset[\t\n\f\r ]*=(?:"([^"]*)"|'([^']*)'|["']|\z|([^\t\n\f\r ;]+))/i =~ value
|
||||
return $1 || $2 || $3
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
##
|
||||
# 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.
|
||||
|
||||
def inflater # :nodoc:
|
||||
return yield @socket unless Gem::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
|
||||
|
||||
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 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
|
||||
|
||||
def read_chunked(dest, chunk_data_io) # :nodoc:
|
||||
total = 0
|
||||
while true
|
||||
line = @socket.readline
|
||||
hexlen = line.slice(/[0-9a-fA-F]+/) or
|
||||
raise Gem::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
|
||||
|
||||
def stream_check
|
||||
raise IOError, 'attempt to read body out of block' if @socket.nil? || @socket.closed?
|
||||
end
|
||||
|
||||
def procdest(dest, block)
|
||||
raise ArgumentError, 'both arg and block given for HTTP method' if
|
||||
dest and block
|
||||
if block
|
||||
Gem::Net::ReadAdapter.new(block)
|
||||
else
|
||||
dest || +''
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Inflater is a wrapper around Gem::Net::BufferedIO that transparently inflates
|
||||
# zlib and gzip streams.
|
||||
|
||||
class Inflater # :nodoc:
|
||||
|
||||
##
|
||||
# Creates a new Inflater wrapping +socket+
|
||||
|
||||
def initialize socket
|
||||
@socket = socket
|
||||
# zlib with automatic gzip detection
|
||||
@inflate = Zlib::Inflate.new(32 + Zlib::MAX_WBITS)
|
||||
end
|
||||
|
||||
##
|
||||
# Finishes the inflate stream.
|
||||
|
||||
def finish
|
||||
return if @inflate.total_in == 0
|
||||
@inflate.finish
|
||||
end
|
||||
|
||||
##
|
||||
# The number of bytes inflated, used to update the Content-Length of
|
||||
# the response.
|
||||
|
||||
def bytes_inflated
|
||||
@inflate.total_out
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a Gem::Net::ReadAdapter that inflates each read chunk into +dest+.
|
||||
#
|
||||
# This allows a large response body to be inflated without storing the
|
||||
# entire body in memory.
|
||||
|
||||
def inflate_adapter(dest)
|
||||
if dest.respond_to?(:set_encoding)
|
||||
dest.set_encoding(Encoding::ASCII_8BIT)
|
||||
elsif dest.respond_to?(:force_encoding)
|
||||
dest.force_encoding(Encoding::ASCII_8BIT)
|
||||
end
|
||||
block = proc do |compressed_chunk|
|
||||
@inflate.inflate(compressed_chunk) do |chunk|
|
||||
compressed_chunk.clear
|
||||
dest << chunk
|
||||
end
|
||||
end
|
||||
|
||||
Gem::Net::ReadAdapter.new(block)
|
||||
end
|
||||
|
||||
##
|
||||
# Reads +clen+ bytes from the socket, inflates them, then writes them to
|
||||
# +dest+. +ignore_eof+ is passed down to Gem::Net::BufferedIO#read
|
||||
#
|
||||
# Unlike Gem::Net::BufferedIO#read, this method returns more than +clen+ bytes.
|
||||
# At this time there is no way for a user of Gem::Net::HTTPResponse to read a
|
||||
# specific number of bytes from the HTTP response body, so this internal
|
||||
# API does not return the same number of bytes as were requested.
|
||||
#
|
||||
# See https://bugs.ruby-lang.org/issues/6492 for further discussion.
|
||||
|
||||
def read clen, dest, ignore_eof = false
|
||||
temp_dest = inflate_adapter(dest)
|
||||
|
||||
@socket.read clen, temp_dest, ignore_eof
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the rest of the socket, inflates it, then writes it to +dest+.
|
||||
|
||||
def read_all dest
|
||||
temp_dest = inflate_adapter(dest)
|
||||
|
||||
@socket.read_all temp_dest
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,84 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../http'
|
||||
|
||||
if $0 == __FILE__
|
||||
require 'open-uri'
|
||||
File.foreach(__FILE__) do |line|
|
||||
puts line
|
||||
break if line.start_with?('end')
|
||||
end
|
||||
puts
|
||||
puts "Gem::Net::HTTP::STATUS_CODES = {"
|
||||
url = "https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv"
|
||||
URI(url).read.each_line do |line|
|
||||
code, mes, = line.split(',')
|
||||
next if ['(Unused)', 'Unassigned', 'Description'].include?(mes)
|
||||
puts " #{code} => '#{mes}',"
|
||||
end
|
||||
puts "} # :nodoc:"
|
||||
end
|
||||
|
||||
Gem::Net::HTTP::STATUS_CODES = {
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing',
|
||||
103 => 'Early Hints',
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
203 => 'Non-Authoritative Information',
|
||||
204 => 'No Content',
|
||||
205 => 'Reset Content',
|
||||
206 => 'Partial Content',
|
||||
207 => 'Multi-Status',
|
||||
208 => 'Already Reported',
|
||||
226 => 'IM Used',
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
307 => 'Temporary Redirect',
|
||||
308 => 'Permanent Redirect',
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
402 => 'Payment Required',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Timeout',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Content Too Large',
|
||||
414 => 'URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
421 => 'Misdirected Request',
|
||||
422 => 'Unprocessable Content',
|
||||
423 => 'Locked',
|
||||
424 => 'Failed Dependency',
|
||||
425 => 'Too Early',
|
||||
426 => 'Upgrade Required',
|
||||
428 => 'Precondition Required',
|
||||
429 => 'Too Many Requests',
|
||||
431 => 'Request Header Fields Too Large',
|
||||
451 => 'Unavailable For Legal Reasons',
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Timeout',
|
||||
505 => 'HTTP Version Not Supported',
|
||||
506 => 'Variant Also Negotiates',
|
||||
507 => 'Insufficient Storage',
|
||||
508 => 'Loop Detected',
|
||||
510 => 'Not Extended (OBSOLETED)',
|
||||
511 => 'Network Authentication Required',
|
||||
} # :nodoc:
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
=begin
|
||||
|
||||
= net/https -- SSL/TLS enhancement for Gem::Net::HTTP.
|
||||
|
||||
This file has been merged with net/http. There is no longer any need to
|
||||
require 'rubygems/net-http/lib/net/https' to use HTTPS.
|
||||
|
||||
See Gem::Net::HTTP for details on how to make HTTPS connections.
|
||||
|
||||
== Info
|
||||
'OpenSSL for Ruby 2' project
|
||||
Copyright (C) 2001 GOTOU Yuuzou <gotoyuzo@notwork.org>
|
||||
All rights reserved.
|
||||
|
||||
== Licence
|
||||
This program is licensed under the same licence as Ruby.
|
||||
(See the file 'LICENCE'.)
|
||||
|
||||
=end
|
||||
|
||||
require_relative 'http'
|
||||
require 'openssl'
|
|
@ -0,0 +1,544 @@
|
|||
# frozen_string_literal: true
|
||||
#
|
||||
# = net/protocol.rb
|
||||
#
|
||||
#--
|
||||
# Copyright (c) 1999-2004 Yukihiro Matsumoto
|
||||
# Copyright (c) 1999-2004 Minero Aoki
|
||||
#
|
||||
# written and maintained by Minero Aoki <aamine@loveruby.net>
|
||||
#
|
||||
# This program is free software. You can re-distribute and/or
|
||||
# modify this program under the same terms as Ruby itself,
|
||||
# Ruby Distribute License or GNU General Public License.
|
||||
#
|
||||
# $Id$
|
||||
#++
|
||||
#
|
||||
# WARNING: This file is going to remove.
|
||||
# Do not rely on the implementation written in this file.
|
||||
#
|
||||
|
||||
require 'socket'
|
||||
require 'timeout'
|
||||
require 'io/wait'
|
||||
|
||||
module Gem::Net # :nodoc:
|
||||
|
||||
class Protocol #:nodoc: internal use only
|
||||
VERSION = "0.2.2"
|
||||
|
||||
private
|
||||
def Protocol.protocol_param(name, val)
|
||||
module_eval(<<-End, __FILE__, __LINE__ + 1)
|
||||
def #{name}
|
||||
#{val}
|
||||
end
|
||||
End
|
||||
end
|
||||
|
||||
def ssl_socket_connect(s, timeout)
|
||||
if timeout
|
||||
while true
|
||||
raise Gem::Net::OpenTimeout if timeout <= 0
|
||||
start = Process.clock_gettime Process::CLOCK_MONOTONIC
|
||||
# to_io is required because SSLSocket doesn't have wait_readable yet
|
||||
case s.connect_nonblock(exception: false)
|
||||
when :wait_readable; s.to_io.wait_readable(timeout)
|
||||
when :wait_writable; s.to_io.wait_writable(timeout)
|
||||
else; break
|
||||
end
|
||||
timeout -= Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
||||
end
|
||||
else
|
||||
s.connect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class ProtocolError < StandardError; end
|
||||
class ProtoSyntaxError < ProtocolError; end
|
||||
class ProtoFatalError < ProtocolError; end
|
||||
class ProtoUnknownError < ProtocolError; end
|
||||
class ProtoServerError < ProtocolError; end
|
||||
class ProtoAuthError < ProtocolError; end
|
||||
class ProtoCommandError < ProtocolError; end
|
||||
class ProtoRetriableError < ProtocolError; end
|
||||
ProtocRetryError = ProtoRetriableError
|
||||
|
||||
##
|
||||
# OpenTimeout, a subclass of Timeout::Error, is raised if a connection cannot
|
||||
# be created within the open_timeout.
|
||||
|
||||
class OpenTimeout < Timeout::Error; end
|
||||
|
||||
##
|
||||
# ReadTimeout, a subclass of Timeout::Error, is raised if a chunk of the
|
||||
# response cannot be read within the read_timeout.
|
||||
|
||||
class ReadTimeout < Timeout::Error
|
||||
def initialize(io = nil)
|
||||
@io = io
|
||||
end
|
||||
attr_reader :io
|
||||
|
||||
def message
|
||||
msg = super
|
||||
if @io
|
||||
msg = "#{msg} with #{@io.inspect}"
|
||||
end
|
||||
msg
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# WriteTimeout, a subclass of Timeout::Error, is raised if a chunk of the
|
||||
# response cannot be written within the write_timeout. Not raised on Windows.
|
||||
|
||||
class WriteTimeout < Timeout::Error
|
||||
def initialize(io = nil)
|
||||
@io = io
|
||||
end
|
||||
attr_reader :io
|
||||
|
||||
def message
|
||||
msg = super
|
||||
if @io
|
||||
msg = "#{msg} with #{@io.inspect}"
|
||||
end
|
||||
msg
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class BufferedIO #:nodoc: internal use only
|
||||
def initialize(io, read_timeout: 60, write_timeout: 60, continue_timeout: nil, debug_output: nil)
|
||||
@io = io
|
||||
@read_timeout = read_timeout
|
||||
@write_timeout = write_timeout
|
||||
@continue_timeout = continue_timeout
|
||||
@debug_output = debug_output
|
||||
@rbuf = ''.b
|
||||
@rbuf_empty = true
|
||||
@rbuf_offset = 0
|
||||
end
|
||||
|
||||
attr_reader :io
|
||||
attr_accessor :read_timeout
|
||||
attr_accessor :write_timeout
|
||||
attr_accessor :continue_timeout
|
||||
attr_accessor :debug_output
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} io=#{@io}>"
|
||||
end
|
||||
|
||||
def eof?
|
||||
@io.eof?
|
||||
end
|
||||
|
||||
def closed?
|
||||
@io.closed?
|
||||
end
|
||||
|
||||
def close
|
||||
@io.close
|
||||
end
|
||||
|
||||
#
|
||||
# Read
|
||||
#
|
||||
|
||||
public
|
||||
|
||||
def read(len, dest = ''.b, ignore_eof = false)
|
||||
LOG "reading #{len} bytes..."
|
||||
read_bytes = 0
|
||||
begin
|
||||
while read_bytes + rbuf_size < len
|
||||
if s = rbuf_consume_all
|
||||
read_bytes += s.bytesize
|
||||
dest << s
|
||||
end
|
||||
rbuf_fill
|
||||
end
|
||||
s = rbuf_consume(len - read_bytes)
|
||||
read_bytes += s.bytesize
|
||||
dest << s
|
||||
rescue EOFError
|
||||
raise unless ignore_eof
|
||||
end
|
||||
LOG "read #{read_bytes} bytes"
|
||||
dest
|
||||
end
|
||||
|
||||
def read_all(dest = ''.b)
|
||||
LOG 'reading all...'
|
||||
read_bytes = 0
|
||||
begin
|
||||
while true
|
||||
if s = rbuf_consume_all
|
||||
read_bytes += s.bytesize
|
||||
dest << s
|
||||
end
|
||||
rbuf_fill
|
||||
end
|
||||
rescue EOFError
|
||||
;
|
||||
end
|
||||
LOG "read #{read_bytes} bytes"
|
||||
dest
|
||||
end
|
||||
|
||||
def readuntil(terminator, ignore_eof = false)
|
||||
offset = @rbuf_offset
|
||||
begin
|
||||
until idx = @rbuf.index(terminator, offset)
|
||||
offset = @rbuf.bytesize
|
||||
rbuf_fill
|
||||
end
|
||||
return rbuf_consume(idx + terminator.bytesize - @rbuf_offset)
|
||||
rescue EOFError
|
||||
raise unless ignore_eof
|
||||
return rbuf_consume
|
||||
end
|
||||
end
|
||||
|
||||
def readline
|
||||
readuntil("\n").chop
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
BUFSIZE = 1024 * 16
|
||||
|
||||
def rbuf_fill
|
||||
tmp = @rbuf_empty ? @rbuf : nil
|
||||
case rv = @io.read_nonblock(BUFSIZE, tmp, exception: false)
|
||||
when String
|
||||
@rbuf_empty = false
|
||||
if rv.equal?(tmp)
|
||||
@rbuf_offset = 0
|
||||
else
|
||||
@rbuf << rv
|
||||
rv.clear
|
||||
end
|
||||
return
|
||||
when :wait_readable
|
||||
(io = @io.to_io).wait_readable(@read_timeout) or raise Gem::Net::ReadTimeout.new(io)
|
||||
# continue looping
|
||||
when :wait_writable
|
||||
# OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable.
|
||||
# http://www.openssl.org/support/faq.html#PROG10
|
||||
(io = @io.to_io).wait_writable(@read_timeout) or raise Gem::Net::ReadTimeout.new(io)
|
||||
# continue looping
|
||||
when nil
|
||||
raise EOFError, 'end of file reached'
|
||||
end while true
|
||||
end
|
||||
|
||||
def rbuf_flush
|
||||
if @rbuf_empty
|
||||
@rbuf.clear
|
||||
@rbuf_offset = 0
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def rbuf_size
|
||||
@rbuf.bytesize - @rbuf_offset
|
||||
end
|
||||
|
||||
def rbuf_consume_all
|
||||
rbuf_consume if rbuf_size > 0
|
||||
end
|
||||
|
||||
def rbuf_consume(len = nil)
|
||||
if @rbuf_offset == 0 && (len.nil? || len == @rbuf.bytesize)
|
||||
s = @rbuf
|
||||
@rbuf = ''.b
|
||||
@rbuf_offset = 0
|
||||
@rbuf_empty = true
|
||||
elsif len.nil?
|
||||
s = @rbuf.byteslice(@rbuf_offset..-1)
|
||||
@rbuf = ''.b
|
||||
@rbuf_offset = 0
|
||||
@rbuf_empty = true
|
||||
else
|
||||
s = @rbuf.byteslice(@rbuf_offset, len)
|
||||
@rbuf_offset += len
|
||||
@rbuf_empty = @rbuf_offset == @rbuf.bytesize
|
||||
rbuf_flush
|
||||
end
|
||||
|
||||
@debug_output << %Q[-> #{s.dump}\n] if @debug_output
|
||||
s
|
||||
end
|
||||
|
||||
#
|
||||
# Write
|
||||
#
|
||||
|
||||
public
|
||||
|
||||
def write(*strs)
|
||||
writing {
|
||||
write0(*strs)
|
||||
}
|
||||
end
|
||||
|
||||
alias << write
|
||||
|
||||
def writeline(str)
|
||||
writing {
|
||||
write0 str + "\r\n"
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def writing
|
||||
@written_bytes = 0
|
||||
@debug_output << '<- ' if @debug_output
|
||||
yield
|
||||
@debug_output << "\n" if @debug_output
|
||||
bytes = @written_bytes
|
||||
@written_bytes = nil
|
||||
bytes
|
||||
end
|
||||
|
||||
def write0(*strs)
|
||||
@debug_output << strs.map(&:dump).join if @debug_output
|
||||
orig_written_bytes = @written_bytes
|
||||
strs.each_with_index do |str, i|
|
||||
need_retry = true
|
||||
case len = @io.write_nonblock(str, exception: false)
|
||||
when Integer
|
||||
@written_bytes += len
|
||||
len -= str.bytesize
|
||||
if len == 0
|
||||
if strs.size == i+1
|
||||
return @written_bytes - orig_written_bytes
|
||||
else
|
||||
need_retry = false
|
||||
# next string
|
||||
end
|
||||
elsif len < 0
|
||||
str = str.byteslice(len, -len)
|
||||
else # len > 0
|
||||
need_retry = false
|
||||
# next string
|
||||
end
|
||||
# continue looping
|
||||
when :wait_writable
|
||||
(io = @io.to_io).wait_writable(@write_timeout) or raise Gem::Net::WriteTimeout.new(io)
|
||||
# continue looping
|
||||
end while need_retry
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Logging
|
||||
#
|
||||
|
||||
private
|
||||
|
||||
def LOG_off
|
||||
@save_debug_out = @debug_output
|
||||
@debug_output = nil
|
||||
end
|
||||
|
||||
def LOG_on
|
||||
@debug_output = @save_debug_out
|
||||
end
|
||||
|
||||
def LOG(msg)
|
||||
return unless @debug_output
|
||||
@debug_output << msg + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class InternetMessageIO < BufferedIO #:nodoc: internal use only
|
||||
def initialize(*, **)
|
||||
super
|
||||
@wbuf = nil
|
||||
end
|
||||
|
||||
#
|
||||
# Read
|
||||
#
|
||||
|
||||
def each_message_chunk
|
||||
LOG 'reading message...'
|
||||
LOG_off()
|
||||
read_bytes = 0
|
||||
while (line = readuntil("\r\n")) != ".\r\n"
|
||||
read_bytes += line.size
|
||||
yield line.delete_prefix('.')
|
||||
end
|
||||
LOG_on()
|
||||
LOG "read message (#{read_bytes} bytes)"
|
||||
end
|
||||
|
||||
# *library private* (cannot handle 'break')
|
||||
def each_list_item
|
||||
while (str = readuntil("\r\n")) != ".\r\n"
|
||||
yield str.chop
|
||||
end
|
||||
end
|
||||
|
||||
def write_message_0(src)
|
||||
prev = @written_bytes
|
||||
each_crlf_line(src) do |line|
|
||||
write0 dot_stuff(line)
|
||||
end
|
||||
@written_bytes - prev
|
||||
end
|
||||
|
||||
#
|
||||
# Write
|
||||
#
|
||||
|
||||
def write_message(src)
|
||||
LOG "writing message from #{src.class}"
|
||||
LOG_off()
|
||||
len = writing {
|
||||
using_each_crlf_line {
|
||||
write_message_0 src
|
||||
}
|
||||
}
|
||||
LOG_on()
|
||||
LOG "wrote #{len} bytes"
|
||||
len
|
||||
end
|
||||
|
||||
def write_message_by_block(&block)
|
||||
LOG 'writing message from block'
|
||||
LOG_off()
|
||||
len = writing {
|
||||
using_each_crlf_line {
|
||||
begin
|
||||
block.call(WriteAdapter.new(self.method(:write_message_0)))
|
||||
rescue LocalJumpError
|
||||
# allow `break' from writer block
|
||||
end
|
||||
}
|
||||
}
|
||||
LOG_on()
|
||||
LOG "wrote #{len} bytes"
|
||||
len
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dot_stuff(s)
|
||||
s.sub(/\A\./, '..')
|
||||
end
|
||||
|
||||
def using_each_crlf_line
|
||||
@wbuf = ''.b
|
||||
yield
|
||||
if not @wbuf.empty? # unterminated last line
|
||||
write0 dot_stuff(@wbuf.chomp) + "\r\n"
|
||||
elsif @written_bytes == 0 # empty src
|
||||
write0 "\r\n"
|
||||
end
|
||||
write0 ".\r\n"
|
||||
@wbuf = nil
|
||||
end
|
||||
|
||||
def each_crlf_line(src)
|
||||
buffer_filling(@wbuf, src) do
|
||||
while line = @wbuf.slice!(/\A[^\r\n]*(?:\n|\r(?:\n|(?!\z)))/)
|
||||
yield line.chomp("\n") + "\r\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def buffer_filling(buf, src)
|
||||
case src
|
||||
when String # for speeding up.
|
||||
0.step(src.size - 1, 1024) do |i|
|
||||
buf << src[i, 1024]
|
||||
yield
|
||||
end
|
||||
when File # for speeding up.
|
||||
while s = src.read(1024)
|
||||
buf << s
|
||||
yield
|
||||
end
|
||||
else # generic reader
|
||||
src.each do |str|
|
||||
buf << str
|
||||
yield if buf.size > 1024
|
||||
end
|
||||
yield unless buf.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# The writer adapter class
|
||||
#
|
||||
class WriteAdapter
|
||||
def initialize(writer)
|
||||
@writer = writer
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class} writer=#{@writer.inspect}>"
|
||||
end
|
||||
|
||||
def write(str)
|
||||
@writer.call(str)
|
||||
end
|
||||
|
||||
alias print write
|
||||
|
||||
def <<(str)
|
||||
write str
|
||||
self
|
||||
end
|
||||
|
||||
def puts(str = '')
|
||||
write str.chomp("\n") + "\n"
|
||||
end
|
||||
|
||||
def printf(*args)
|
||||
write sprintf(*args)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class ReadAdapter #:nodoc: internal use only
|
||||
def initialize(block)
|
||||
@block = block
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<#{self.class}>"
|
||||
end
|
||||
|
||||
def <<(str)
|
||||
call_block(str, &@block) if @block
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This method is needed because @block must be called by yield,
|
||||
# not Proc#call. You can see difference when using `break' in
|
||||
# the block.
|
||||
def call_block(str)
|
||||
yield str
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module NetPrivate #:nodoc: obsolete
|
||||
Socket = ::Gem::Net::InternetMessageIO
|
||||
end
|
||||
|
||||
end # module Gem::Net
|
|
@ -0,0 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../net-http/lib/net/http"
|
|
@ -74,7 +74,7 @@ class Gem::RemoteFetcher
|
|||
|
||||
def initialize(proxy=nil, dns=nil, headers={})
|
||||
require_relative "core_ext/tcpsocket_init" if Gem.configuration.ipv4_fallback_enabled
|
||||
require "net/http"
|
||||
require_relative "net/http"
|
||||
require "stringio"
|
||||
require "uri"
|
||||
|
||||
|
@ -210,17 +210,17 @@ class Gem::RemoteFetcher
|
|||
# HTTP Fetcher. Dispatched by +fetch_path+. Use it instead.
|
||||
|
||||
def fetch_http(uri, last_modified = nil, head = false, depth = 0)
|
||||
fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get
|
||||
fetch_type = head ? Gem::Net::HTTP::Head : Gem::Net::HTTP::Get
|
||||
response = request uri, fetch_type, last_modified do |req|
|
||||
headers.each {|k,v| req.add_field(k,v) }
|
||||
end
|
||||
|
||||
case response
|
||||
when Net::HTTPOK, Net::HTTPNotModified then
|
||||
when Gem::Net::HTTPOK, Gem::Net::HTTPNotModified then
|
||||
response.uri = uri
|
||||
head ? response : response.body
|
||||
when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther,
|
||||
Net::HTTPTemporaryRedirect then
|
||||
when Gem::Net::HTTPMovedPermanently, Gem::Net::HTTPFound, Gem::Net::HTTPSeeOther,
|
||||
Gem::Net::HTTPTemporaryRedirect then
|
||||
raise FetchError.new("too many redirects", uri) if depth > 10
|
||||
|
||||
unless location = response["Location"]
|
||||
|
@ -305,8 +305,8 @@ class Gem::RemoteFetcher
|
|||
end
|
||||
|
||||
##
|
||||
# Performs a Net::HTTP request of type +request_class+ on +uri+ returning
|
||||
# a Net::HTTP response object. request maintains a table of persistent
|
||||
# Performs a Gem::Net::HTTP request of type +request_class+ on +uri+ returning
|
||||
# a Gem::Net::HTTP response object. request maintains a table of persistent
|
||||
# connections to reduce connect overhead.
|
||||
|
||||
def request(uri, request_class, last_modified = nil)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "net/http"
|
||||
require_relative "net/http"
|
||||
require_relative "user_interaction"
|
||||
|
||||
class Gem::Request
|
||||
|
@ -205,7 +205,7 @@ class Gem::Request
|
|||
if request.response_body_permitted? && file_name =~ /\.gem$/
|
||||
reporter = ui.download_reporter
|
||||
response = connection.request(request) do |incomplete_response|
|
||||
if Net::HTTPOK === incomplete_response
|
||||
if Gem::Net::HTTPOK === incomplete_response
|
||||
reporter.fetch(file_name, incomplete_response.content_length)
|
||||
downloaded = 0
|
||||
data = String.new
|
||||
|
@ -228,7 +228,7 @@ class Gem::Request
|
|||
end
|
||||
|
||||
verbose "#{response.code} #{response.message}"
|
||||
rescue Net::HTTPBadResponse
|
||||
rescue Gem::Net::HTTPBadResponse
|
||||
verbose "bad response"
|
||||
|
||||
reset connection
|
||||
|
@ -237,11 +237,11 @@ class Gem::Request
|
|||
|
||||
bad_response = true
|
||||
retry
|
||||
rescue Net::HTTPFatalError
|
||||
rescue Gem::Net::HTTPFatalError
|
||||
verbose "fatal error"
|
||||
|
||||
raise Gem::RemoteFetcher::FetchError.new("fatal error", @uri)
|
||||
# HACK: work around EOFError bug in Net::HTTP
|
||||
# HACK: work around EOFError bug in Gem::Net::HTTP
|
||||
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
||||
# to install gems.
|
||||
rescue EOFError, Timeout::Error,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Gem::Request::ConnectionPools # :nodoc:
|
||||
@client = Net::HTTP
|
||||
@client = Gem::Net::HTTP
|
||||
|
||||
class << self
|
||||
attr_accessor :client
|
||||
|
|
|
@ -140,7 +140,7 @@ class Gem::S3URISigner
|
|||
end
|
||||
|
||||
def ec2_metadata_credentials_json
|
||||
require "net/http"
|
||||
require_relative "net/http"
|
||||
require_relative "request"
|
||||
require_relative "request/connection_pools"
|
||||
require "json"
|
||||
|
@ -154,11 +154,11 @@ class Gem::S3URISigner
|
|||
def ec2_metadata_request(url)
|
||||
uri = URI(url)
|
||||
@request_pool ||= create_request_pool(uri)
|
||||
request = Gem::Request.new(uri, Net::HTTP::Get, nil, @request_pool)
|
||||
request = Gem::Request.new(uri, Gem::Net::HTTP::Get, nil, @request_pool)
|
||||
response = request.fetch
|
||||
|
||||
case response
|
||||
when Net::HTTPOK then
|
||||
when Gem::Net::HTTPOK then
|
||||
JSON.parse(response.body)
|
||||
else
|
||||
raise InstanceProfileError.new("Unable to fetch AWS metadata from #{uri}: #{response.message} #{response.code}")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "helper"
|
||||
require "net/http"
|
||||
require "rubygems/net/http"
|
||||
require "rubygems/openssl"
|
||||
|
||||
unless Gem::HAVE_OPENSSL
|
||||
|
@ -28,7 +28,7 @@ class TestGemBundledCA < Gem::TestCase
|
|||
|
||||
def assert_https(host)
|
||||
assert true
|
||||
http = Net::HTTP.new(host, 443)
|
||||
http = Gem::Net::HTTP.new(host, 443)
|
||||
http.use_ssl = true
|
||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||
http.cert_store = bundled_certificate_store
|
||||
|
|
|
@ -44,7 +44,7 @@ EOF
|
|||
@cmd.show_owners("freewill")
|
||||
end
|
||||
|
||||
assert_equal Net::HTTP::Get, @stub_fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Get, @stub_fetcher.last_request.class
|
||||
assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
|
||||
|
||||
assert_match(/Owners for gem: freewill/, @stub_ui.output)
|
||||
|
@ -165,7 +165,7 @@ EOF
|
|||
@cmd.add_owners("freewill", ["user-new1@example.com"])
|
||||
end
|
||||
|
||||
assert_equal Net::HTTP::Post, @stub_fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Post, @stub_fetcher.last_request.class
|
||||
assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
|
||||
assert_equal "email=user-new1%40example.com", @stub_fetcher.last_request.body
|
||||
|
||||
|
@ -244,7 +244,7 @@ EOF
|
|||
@cmd.remove_owners("freewill", ["user-remove1@example.com"])
|
||||
end
|
||||
|
||||
assert_equal Net::HTTP::Delete, @stub_fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Delete, @stub_fetcher.last_request.class
|
||||
assert_equal Gem.configuration.rubygems_api_key, @stub_fetcher.last_request["Authorization"]
|
||||
assert_equal "email=user-remove1%40example.com", @stub_fetcher.last_request.body
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
|
|||
|
||||
assert_match(/Pushing gem to #{@host}.../, @ui.output)
|
||||
|
||||
assert_equal Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
|
||||
assert_equal File.size(@path), @fetcher.last_request["Content-Length"].to_i
|
||||
assert_equal "application/octet-stream", @fetcher.last_request["Content-Type"]
|
||||
|
@ -77,7 +77,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
|
|||
|
||||
@cmd.execute
|
||||
|
||||
assert_equal Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
|
||||
assert_equal "application/octet-stream",
|
||||
@fetcher.last_request["Content-Type"]
|
||||
|
@ -96,7 +96,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
|
|||
|
||||
@cmd.execute
|
||||
|
||||
assert_equal Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
|
||||
assert_equal "application/octet-stream",
|
||||
@fetcher.last_request["Content-Type"]
|
||||
|
@ -116,7 +116,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
|
|||
|
||||
@cmd.execute
|
||||
|
||||
assert_equal Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
|
||||
assert_equal "application/octet-stream",
|
||||
@fetcher.last_request["Content-Type"]
|
||||
|
@ -319,7 +319,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
|
|||
|
||||
assert_match(/Pushing gem to #{host}.../, @ui.output)
|
||||
|
||||
assert_equal Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem::Net::HTTP::Post, @fetcher.last_request.class
|
||||
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
|
||||
assert_equal File.size(@path), @fetcher.last_request["Content-Length"].to_i
|
||||
assert_equal "application/octet-stream", @fetcher.last_request["Content-Type"]
|
||||
|
|
|
@ -488,7 +488,7 @@ class TestGemDependencyInstaller < Gem::TestCase
|
|||
@fetcher.data["http://gems.example.com/gems/a-1.gem"] = a1_data
|
||||
|
||||
# compact index is available
|
||||
compact_index_response = Net::HTTPResponse.new "1.1", 200, "OK"
|
||||
compact_index_response = Gem::Net::HTTPResponse.new "1.1", 200, "OK"
|
||||
compact_index_response.uri = URI("http://gems.example.com")
|
||||
@fetcher.data["http://gems.example.com/"] = compact_index_response
|
||||
|
||||
|
|
|
@ -659,13 +659,13 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
def fetcher.request(uri, request_class, last_modified = nil)
|
||||
url = "http://gems.example.com/redirect"
|
||||
if defined? @requested
|
||||
res = Net::HTTPOK.new nil, 200, nil
|
||||
res = Gem::Net::HTTPOK.new nil, 200, nil
|
||||
def res.body
|
||||
"real_path"
|
||||
end
|
||||
else
|
||||
@requested = true
|
||||
res = Net::HTTPMovedPermanently.new nil, 301, nil
|
||||
res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
|
||||
res.add_field "Location", url
|
||||
end
|
||||
res
|
||||
|
@ -683,7 +683,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
|
||||
def fetcher.request(uri, request_class, last_modified = nil)
|
||||
url = "http://gems.example.com/redirect"
|
||||
res = Net::HTTPMovedPermanently.new nil, 301, nil
|
||||
res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
|
||||
res.add_field "Location", url
|
||||
res
|
||||
end
|
||||
|
@ -701,7 +701,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
url = "http://gems.example.com/redirect"
|
||||
|
||||
def fetcher.request(uri, request_class, last_modified = nil)
|
||||
res = Net::HTTPMovedPermanently.new nil, 301, nil
|
||||
res = Gem::Net::HTTPMovedPermanently.new nil, 301, nil
|
||||
res
|
||||
end
|
||||
|
||||
|
@ -728,7 +728,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
|
||||
def fetcher.request(uri, request_class, last_modified = nil)
|
||||
$fetched_uri = uri
|
||||
res = Net::HTTPOK.new nil, 200, nil
|
||||
res = Gem::Net::HTTPOK.new nil, 200, nil
|
||||
def res.body
|
||||
"success"
|
||||
end
|
||||
|
@ -958,8 +958,8 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
@fetcher = fetcher
|
||||
|
||||
assert_throws :block_called do
|
||||
fetcher.request URI("http://example"), Net::HTTP::Get do |req|
|
||||
assert_kind_of Net::HTTPGenericRequest, req
|
||||
fetcher.request URI("http://example"), Gem::Net::HTTP::Get do |req|
|
||||
assert_kind_of Gem::Net::HTTPGenericRequest, req
|
||||
throw :block_called
|
||||
end
|
||||
end
|
||||
|
|
|
@ -96,7 +96,7 @@ class TestGemRequest < Gem::TestCase
|
|||
end
|
||||
|
||||
def test_configure_connection_for_https
|
||||
connection = Net::HTTP.new "localhost", 443
|
||||
connection = Gem::Net::HTTP.new "localhost", 443
|
||||
|
||||
request = Class.new(Gem::Request) do
|
||||
def self.get_cert_files
|
||||
|
@ -115,7 +115,7 @@ class TestGemRequest < Gem::TestCase
|
|||
ssl_ca_cert = Gem.configuration.ssl_ca_cert
|
||||
Gem.configuration.ssl_ca_cert = CA_CERT_FILE
|
||||
|
||||
connection = Net::HTTP.new "localhost", 443
|
||||
connection = Gem::Net::HTTP.new "localhost", 443
|
||||
|
||||
request = Class.new(Gem::Request) do
|
||||
def self.get_cert_files
|
||||
|
@ -193,7 +193,7 @@ class TestGemRequest < Gem::TestCase
|
|||
def test_fetch
|
||||
uri = Gem::Uri.new(URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
|
||||
response = util_stub_net_http(body: :junk, code: 200) do
|
||||
@request = make_request(uri, Net::HTTP::Get, nil, nil)
|
||||
@request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
|
||||
|
||||
@request.fetch
|
||||
end
|
||||
|
@ -207,7 +207,7 @@ class TestGemRequest < Gem::TestCase
|
|||
uri = Gem::Uri.new(URI.parse("https://user:pass@example.rubygems/specs.#{Gem.marshal_version}"))
|
||||
conn = util_stub_net_http(body: :junk, code: 200) do |c|
|
||||
use_ui @ui do
|
||||
@request = make_request(uri, Net::HTTP::Get, nil, nil)
|
||||
@request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
|
||||
@request.fetch
|
||||
end
|
||||
c
|
||||
|
@ -224,7 +224,7 @@ class TestGemRequest < Gem::TestCase
|
|||
|
||||
conn = util_stub_net_http(body: :junk, code: 200) do |c|
|
||||
use_ui @ui do
|
||||
@request = make_request(uri, Net::HTTP::Get, nil, nil)
|
||||
@request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
|
||||
@request.fetch
|
||||
end
|
||||
c
|
||||
|
@ -241,7 +241,7 @@ class TestGemRequest < Gem::TestCase
|
|||
|
||||
conn = util_stub_net_http(body: :junk, code: 200) do |c|
|
||||
use_ui @ui do
|
||||
@request = make_request(uri, Net::HTTP::Get, nil, nil)
|
||||
@request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
|
||||
@request.fetch
|
||||
end
|
||||
c
|
||||
|
@ -255,7 +255,7 @@ class TestGemRequest < Gem::TestCase
|
|||
def test_fetch_head
|
||||
uri = Gem::Uri.new(URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
|
||||
response = util_stub_net_http(body: "", code: 200) do |_conn|
|
||||
@request = make_request(uri, Net::HTTP::Get, nil, nil)
|
||||
@request = make_request(uri, Gem::Net::HTTP::Get, nil, nil)
|
||||
@request.fetch
|
||||
end
|
||||
|
||||
|
@ -267,7 +267,7 @@ class TestGemRequest < Gem::TestCase
|
|||
uri = Gem::Uri.new(URI.parse("#{@gem_repo}/specs.#{Gem.marshal_version}"))
|
||||
t = Time.utc(2013, 1, 2, 3, 4, 5)
|
||||
conn, response = util_stub_net_http(body: "", code: 304) do |c|
|
||||
@request = make_request(uri, Net::HTTP::Get, t, nil)
|
||||
@request = make_request(uri, Gem::Net::HTTP::Get, t, nil)
|
||||
[c, @request.fetch]
|
||||
end
|
||||
|
||||
|
|
|
@ -437,19 +437,19 @@ class TestGemRequirement < Gem::TestCase
|
|||
end
|
||||
|
||||
def test_marshal_load_attack
|
||||
wa = Net::WriteAdapter.allocate
|
||||
wa = Gem::Net::WriteAdapter.allocate
|
||||
wa.instance_variable_set(:@socket, self.class)
|
||||
wa.instance_variable_set(:@method_id, :exploit)
|
||||
request_set = Gem::RequestSet.allocate
|
||||
request_set.instance_variable_set(:@git_set, "id")
|
||||
request_set.instance_variable_set(:@sets, wa)
|
||||
wa = Net::WriteAdapter.allocate
|
||||
wa = Gem::Net::WriteAdapter.allocate
|
||||
wa.instance_variable_set(:@socket, request_set)
|
||||
wa.instance_variable_set(:@method_id, :resolve)
|
||||
ent = Gem::Package::TarReader::Entry.allocate
|
||||
ent.instance_variable_set(:@read, 0)
|
||||
ent.instance_variable_set(:@header, "aaa")
|
||||
io = Net::BufferedIO.allocate
|
||||
io = Gem::Net::BufferedIO.allocate
|
||||
io.instance_variable_set(:@io, ent)
|
||||
io.instance_variable_set(:@debug_output, wa)
|
||||
reader = Gem::Package::TarReader.allocate
|
||||
|
|
|
@ -43,7 +43,7 @@ class TestGemSource < Gem::TestCase
|
|||
end
|
||||
|
||||
def test_dependency_resolver_set_bundler_api
|
||||
response = Net::HTTPResponse.new "1.1", 200, "OK"
|
||||
response = Gem::Net::HTTPResponse.new "1.1", 200, "OK"
|
||||
response.uri = URI("http://example")
|
||||
|
||||
@fetcher.data[@gem_repo] = response
|
||||
|
|
|
@ -21,7 +21,7 @@ class TestGemSourceSubpathProblem < Gem::TestCase
|
|||
end
|
||||
|
||||
def test_dependency_resolver_set
|
||||
response = Net::HTTPResponse.new "1.1", 200, "OK"
|
||||
response = Gem::Net::HTTPResponse.new "1.1", 200, "OK"
|
||||
response.uri = URI("http://example")
|
||||
|
||||
@fetcher.data["#{@gem_repo}/"] = response
|
||||
|
|
|
@ -35,15 +35,15 @@ class WebauthnListenerTest < Gem::TestCase
|
|||
|
||||
def test_wait_for_otp_code_get_follows_options
|
||||
wait_for_otp_code
|
||||
assert Gem::MockBrowser.options(URI("http://localhost:#{@port}?code=xyz")).is_a? Net::HTTPNoContent
|
||||
assert Gem::MockBrowser.get(URI("http://localhost:#{@port}?code=xyz")).is_a? Net::HTTPOK
|
||||
assert Gem::MockBrowser.options(URI("http://localhost:#{@port}?code=xyz")).is_a? Gem::Net::HTTPNoContent
|
||||
assert Gem::MockBrowser.get(URI("http://localhost:#{@port}?code=xyz")).is_a? Gem::Net::HTTPOK
|
||||
end
|
||||
|
||||
def test_wait_for_otp_code_options_request
|
||||
wait_for_otp_code
|
||||
response = Gem::MockBrowser.options URI("http://localhost:#{@port}?code=xyz")
|
||||
|
||||
assert response.is_a? Net::HTTPNoContent
|
||||
assert response.is_a? Gem::Net::HTTPNoContent
|
||||
assert_equal Gem.host, response["access-control-allow-origin"]
|
||||
assert_equal "POST", response["access-control-allow-methods"]
|
||||
assert_equal "Content-Type, Authorization, x-csrf-token", response["access-control-allow-headers"]
|
||||
|
@ -54,7 +54,7 @@ class WebauthnListenerTest < Gem::TestCase
|
|||
wait_for_otp_code
|
||||
response = Gem::MockBrowser.get URI("http://localhost:#{@port}?code=xyz")
|
||||
|
||||
assert response.is_a? Net::HTTPOK
|
||||
assert response.is_a? Gem::Net::HTTPOK
|
||||
assert_equal "text/plain; charset=utf-8", response["Content-Type"]
|
||||
assert_equal "7", response["Content-Length"]
|
||||
assert_equal Gem.host, response["access-control-allow-origin"]
|
||||
|
@ -72,7 +72,7 @@ class WebauthnListenerTest < Gem::TestCase
|
|||
response = Gem::MockBrowser.post URI("http://localhost:#{@port}?code=xyz")
|
||||
|
||||
assert response
|
||||
assert response.is_a? Net::HTTPMethodNotAllowed
|
||||
assert response.is_a? Gem::Net::HTTPMethodNotAllowed
|
||||
assert_equal "GET, OPTIONS", response["allow"]
|
||||
assert_equal "close", response["Connection"]
|
||||
|
||||
|
@ -84,7 +84,7 @@ class WebauthnListenerTest < Gem::TestCase
|
|||
wait_for_otp_code_expect_error_with_message("Security device verification failed: Page at /path not found.")
|
||||
response = Gem::MockBrowser.post URI("http://localhost:#{@port}/path?code=xyz")
|
||||
|
||||
assert response.is_a? Net::HTTPNotFound
|
||||
assert response.is_a? Gem::Net::HTTPNotFound
|
||||
assert_equal "close", response["Connection"]
|
||||
|
||||
@thread.join
|
||||
|
@ -95,7 +95,7 @@ class WebauthnListenerTest < Gem::TestCase
|
|||
wait_for_otp_code_expect_error_with_message("Security device verification failed: Did not receive OTP from https://rubygems.org.")
|
||||
response = Gem::MockBrowser.get URI("http://localhost:#{@port}")
|
||||
|
||||
assert response.is_a? Net::HTTPBadRequest
|
||||
assert response.is_a? Gem::Net::HTTPBadRequest
|
||||
assert_equal "text/plain; charset=utf-8", response["Content-Type"]
|
||||
assert_equal "22", response["Content-Length"]
|
||||
assert_equal "close", response["Connection"]
|
||||
|
@ -109,7 +109,7 @@ class WebauthnListenerTest < Gem::TestCase
|
|||
wait_for_otp_code_expect_error_with_message("Security device verification failed: Did not receive OTP from https://rubygems.org.")
|
||||
response = Gem::MockBrowser.get URI("http://localhost:#{@port}?param=xyz")
|
||||
|
||||
assert response.is_a? Net::HTTPBadRequest
|
||||
assert response.is_a? Gem::Net::HTTPBadRequest
|
||||
assert_equal "text/plain; charset=utf-8", response["Content-Type"]
|
||||
assert_equal "22", response["Content-Length"]
|
||||
assert_equal "close", response["Connection"]
|
||||
|
|
|
@ -65,7 +65,7 @@ class Gem::FakeFetcher
|
|||
def create_response(uri)
|
||||
data = find_data(uri)
|
||||
response = data.respond_to?(:call) ? data.call : data
|
||||
raise TypeError, "#{response.class} is not a type of Net::HTTPResponse" unless response.is_a?(Net::HTTPResponse)
|
||||
raise TypeError, "#{response.class} is not a type of Gem::Net::HTTPResponse" unless response.is_a?(Gem::Net::HTTPResponse)
|
||||
|
||||
response
|
||||
end
|
||||
|
@ -164,7 +164,7 @@ class Gem::FakeFetcher
|
|||
end
|
||||
|
||||
##
|
||||
# The HTTPResponseFactory allows easy creation of Net::HTTPResponse instances in RubyGems tests:
|
||||
# The HTTPResponseFactory allows easy creation of Gem::Net::HTTPResponse instances in RubyGems tests:
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
|
@ -178,7 +178,7 @@ end
|
|||
|
||||
class Gem::HTTPResponseFactory
|
||||
def self.create(body:, code:, msg:, headers: {})
|
||||
response = Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
|
||||
response = Gem::Net::HTTPResponse.send(:response_class, code.to_s).new("1.0", code.to_s, msg)
|
||||
response.instance_variable_set(:@body, body)
|
||||
response.instance_variable_set(:@read, true)
|
||||
headers.each {|name, value| response[name] = value }
|
||||
|
@ -201,23 +201,23 @@ end
|
|||
|
||||
class Gem::MockBrowser
|
||||
def self.options(uri)
|
||||
options = Net::HTTP::Options.new(uri)
|
||||
Net::HTTP.start(uri.hostname, uri.port) do |http|
|
||||
options = Gem::Net::HTTP::Options.new(uri)
|
||||
Gem::Net::HTTP.start(uri.hostname, uri.port) do |http|
|
||||
http.request(options)
|
||||
end
|
||||
end
|
||||
|
||||
def self.get(uri)
|
||||
get = Net::HTTP::Get.new(uri)
|
||||
Net::HTTP.start(uri.hostname, uri.port) do |http|
|
||||
get = Gem::Net::HTTP::Get.new(uri)
|
||||
Gem::Net::HTTP.start(uri.hostname, uri.port) do |http|
|
||||
http.request(get)
|
||||
end
|
||||
end
|
||||
|
||||
def self.post(uri, content_type: "application/x-www-form-urlencoded")
|
||||
headers = { "content-type" => content_type } if content_type
|
||||
post = Net::HTTP::Post.new(uri, headers)
|
||||
Net::HTTP.start(uri.hostname, uri.port) do |http|
|
||||
post = Gem::Net::HTTP::Post.new(uri, headers)
|
||||
Gem::Net::HTTP.start(uri.hostname, uri.port) do |http|
|
||||
http.request(post)
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче