[rubygems/rubygems] Vendor net-http and net-protocol in RubyGems

https://github.com/rubygems/rubygems/commit/99d91c9ed2
This commit is contained in:
David Rodríguez 2023-01-29 21:24:11 +01:00 коммит произвёл Hiroshi SHIBATA
Родитель 6cefad7704
Коммит ce924ce1fb
33 изменённых файлов: 7158 добавлений и 75 удалений

Просмотреть файл

@ -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

3
lib/rubygems/net/http.rb Normal file
Просмотреть файл

@ -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