summaryrefslogtreecommitdiff
path: root/lib/net/http.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/net/http.rb')
-rw-r--r--lib/net/http.rb278
1 files changed, 195 insertions, 83 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 387df4b8f4..53295fe90c 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -37,7 +37,7 @@ module Net #:nodoc:
# For information about \HTTP, see:
#
# - {Hypertext Transfer Protocol}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol].
- # - {Technical overview}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technical_overview].
+ # - {Technology}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Technology].
#
# == About the Examples
#
@@ -67,10 +67,12 @@ module Net #:nodoc:
# Net::HTTP.post(uri, data)
# params = {title: 'foo', body: 'bar', userId: 1}
# Net::HTTP.post_form(uri, params)
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # Net::HTTP.put(uri, data)
#
# - If performance is important, consider using sessions, which lower request overhead.
# This {session}[rdoc-ref:Net::HTTP@Sessions] has multiple requests for
- # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]
+ # {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Method]
# and {WebDAV methods}[https://en.wikipedia.org/wiki/WebDAV#Implementation]:
#
# Net::HTTP.start(hostname) do |http|
@@ -196,7 +198,7 @@ module Net #:nodoc:
# In the block, you can use these instance methods,
# each of which that sends a single request:
#
- # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods]:
+ # - {HTTP methods}[https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Method]:
#
# - #get, #request_get: GET.
# - #head, #request_head: HEAD.
@@ -445,7 +447,7 @@ module Net #:nodoc:
# if the response has header <tt>'Content-Range'</tt>.
#
# Otherwise decompression (or not) depends on the value of header
- # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#content-encoding-response-header]:
+ # {Content-Encoding}[https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Content-Encoding_2]:
#
# - <tt>'deflate'</tt>, <tt>'gzip'</tt>, or <tt>'x-gzip'</tt>:
# decompresses the body and deletes the header.
@@ -456,6 +458,10 @@ module Net #:nodoc:
#
# == What's Here
#
+ # First, what's elsewhere. Class Net::HTTP:
+ #
+ # - Inherits from {class Object}[rdoc-ref:Object#class-object-whats-here].
+ #
# This is a categorized summary of methods and attributes.
#
# === \Net::HTTP Objects
@@ -469,8 +475,7 @@ module Net #:nodoc:
#
# - {::start}[rdoc-ref:Net::HTTP.start]:
# Begins a new session in a new \Net::HTTP object.
- # - {#started?}[rdoc-ref:Net::HTTP#started?]
- # (aliased as {#active?}[rdoc-ref:Net::HTTP#active?]):
+ # - {#started?}[rdoc-ref:Net::HTTP#started?]:
# Returns whether in a session.
# - {#finish}[rdoc-ref:Net::HTTP#finish]:
# Ends an active session.
@@ -520,6 +525,8 @@ module Net #:nodoc:
# Sends a POST request with form data and returns a response object.
# - {::post}[rdoc-ref:Net::HTTP.post]:
# Sends a POST request with data and returns a response object.
+ # - {::put}[rdoc-ref:Net::HTTP.put]:
+ # Sends a PUT request with data and returns a response object.
# - {#copy}[rdoc-ref:Net::HTTP#copy]:
# Sends a COPY request and returns a response object.
# - {#delete}[rdoc-ref:Net::HTTP#delete]:
@@ -548,18 +555,15 @@ module Net #:nodoc:
# Sends a PUT request and returns a response object.
# - {#request}[rdoc-ref:Net::HTTP#request]:
# Sends a request and returns a response object.
- # - {#request_get}[rdoc-ref:Net::HTTP#request_get]
- # (aliased as {#get2}[rdoc-ref:Net::HTTP#get2]):
+ # - {#request_get}[rdoc-ref:Net::HTTP#request_get]:
# Sends a GET request and forms a response object;
# if a block given, calls the block with the object,
# otherwise returns the object.
- # - {#request_head}[rdoc-ref:Net::HTTP#request_head]
- # (aliased as {#head2}[rdoc-ref:Net::HTTP#head2]):
+ # - {#request_head}[rdoc-ref:Net::HTTP#request_head]:
# Sends a HEAD request and forms a response object;
# if a block given, calls the block with the object,
# otherwise returns the object.
- # - {#request_post}[rdoc-ref:Net::HTTP#request_post]
- # (aliased as {#post2}[rdoc-ref:Net::HTTP#post2]):
+ # - {#request_post}[rdoc-ref:Net::HTTP#request_post]:
# Sends a POST request and forms a response object;
# if a block given, calls the block with the object,
# otherwise returns the object.
@@ -597,8 +601,7 @@ module Net #:nodoc:
# Returns whether +self+ is a proxy class.
# - {#proxy?}[rdoc-ref:Net::HTTP#proxy?]:
# Returns whether +self+ has a proxy.
- # - {#proxy_address}[rdoc-ref:Net::HTTP#proxy_address]
- # (aliased as {#proxyaddr}[rdoc-ref:Net::HTTP#proxyaddr]):
+ # - {#proxy_address}[rdoc-ref:Net::HTTP#proxy_address]:
# Returns the proxy address.
# - {#proxy_from_env?}[rdoc-ref:Net::HTTP#proxy_from_env?]:
# Returns whether the proxy is taken from an environment variable.
@@ -710,8 +713,7 @@ module Net #:nodoc:
# === \HTTP Version
#
# - {::version_1_2?}[rdoc-ref:Net::HTTP.version_1_2?]
- # (aliased as {::is_version_1_2?}[rdoc-ref:Net::HTTP.is_version_1_2?]
- # and {::version_1_2}[rdoc-ref:Net::HTTP.version_1_2]):
+ # (aliased as {::version_1_2}[rdoc-ref:Net::HTTP.version_1_2]):
# Returns true; retained for compatibility.
#
# === Debugging
@@ -722,7 +724,7 @@ module Net #:nodoc:
class HTTP < Protocol
# :stopdoc:
- VERSION = "0.4.1"
+ VERSION = "0.9.1"
HTTPVersion = '1.1'
begin
require 'zlib'
@@ -889,6 +891,39 @@ module Net #:nodoc:
}
end
+ # Sends a PUT request to the server; returns a Net::HTTPResponse object.
+ #
+ # Argument +url+ must be a URL;
+ # argument +data+ must be a string:
+ #
+ # _uri = uri.dup
+ # _uri.path = '/posts'
+ # data = '{"title": "foo", "body": "bar", "userId": 1}'
+ # headers = {'content-type': 'application/json'}
+ # res = Net::HTTP.put(_uri, data, headers) # => #<Net::HTTPCreated 201 Created readbody=true>
+ # puts res.body
+ #
+ # Output:
+ #
+ # {
+ # "title": "foo",
+ # "body": "bar",
+ # "userId": 1,
+ # "id": 101
+ # }
+ #
+ # Related:
+ #
+ # - Net::HTTP::Put: request class for \HTTP method +PUT+.
+ # - Net::HTTP#put: convenience method for \HTTP method +PUT+.
+ #
+ def HTTP.put(url, data, header = nil)
+ start(url.hostname, url.port,
+ :use_ssl => url.scheme == 'https' ) {|http|
+ http.put(url, data, header)
+ }
+ end
+
#
# \HTTP session management
#
@@ -1062,7 +1097,7 @@ module Net #:nodoc:
# For proxy-defining arguments +p_addr+ through +p_no_proxy+,
# see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
#
- def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
+ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil, p_use_ssl = nil)
http = super address, port
if proxy_class? then # from Net::HTTP::Proxy()
@@ -1071,6 +1106,7 @@ module Net #:nodoc:
http.proxy_port = @proxy_port
http.proxy_user = @proxy_user
http.proxy_pass = @proxy_pass
+ http.proxy_use_ssl = @proxy_use_ssl
elsif p_addr == :ENV then
http.proxy_from_env = true
else
@@ -1082,34 +1118,68 @@ module Net #:nodoc:
http.proxy_port = p_port || default_port
http.proxy_user = p_user
http.proxy_pass = p_pass
+ http.proxy_use_ssl = p_use_ssl
end
http
end
+ class << HTTP
+ # Allows to set the default configuration that will be used
+ # when creating a new connection.
+ #
+ # Example:
+ #
+ # Net::HTTP.default_configuration = {
+ # read_timeout: 1,
+ # write_timeout: 1
+ # }
+ # http = Net::HTTP.new(hostname)
+ # http.open_timeout # => 60
+ # http.read_timeout # => 1
+ # http.write_timeout # => 1
+ #
+ attr_accessor :default_configuration
+ end
+
# Creates a new \Net::HTTP object for the specified server address,
# without opening the TCP connection or initializing the \HTTP session.
# The +address+ should be a DNS hostname or IP address.
def initialize(address, port = nil) # :nodoc:
+ defaults = {
+ keep_alive_timeout: 2,
+ close_on_empty_response: false,
+ open_timeout: 60,
+ read_timeout: 60,
+ write_timeout: 60,
+ continue_timeout: nil,
+ max_retries: 1,
+ debug_output: nil,
+ response_body_encoding: false,
+ ignore_eof: true
+ }
+ options = defaults.merge(self.class.default_configuration || {})
+
@address = address
@port = (port || HTTP.default_port)
@ipaddr = nil
@local_host = nil
@local_port = nil
@curr_http_version = HTTPVersion
- @keep_alive_timeout = 2
+ @keep_alive_timeout = options[:keep_alive_timeout]
@last_communicated = nil
- @close_on_empty_response = false
+ @close_on_empty_response = options[:close_on_empty_response]
@socket = nil
@started = false
- @open_timeout = 60
- @read_timeout = 60
- @write_timeout = 60
- @continue_timeout = nil
- @max_retries = 1
- @debug_output = nil
- @response_body_encoding = false
- @ignore_eof = true
+ @open_timeout = options[:open_timeout]
+ @read_timeout = options[:read_timeout]
+ @write_timeout = options[:write_timeout]
+ @continue_timeout = options[:continue_timeout]
+ @max_retries = options[:max_retries]
+ @debug_output = options[:debug_output]
+ @response_body_encoding = options[:response_body_encoding]
+ @ignore_eof = options[:ignore_eof]
+ @tcpsocket_supports_open_timeout = nil
@proxy_from_env = false
@proxy_uri = nil
@@ -1117,6 +1187,7 @@ module Net #:nodoc:
@proxy_port = nil
@proxy_user = nil
@proxy_pass = nil
+ @proxy_use_ssl = nil
@use_ssl = false
@ssl_context = nil
@@ -1233,7 +1304,7 @@ module Net #:nodoc:
# Sets whether to determine the proxy from environment variable
# '<tt>ENV['http_proxy']</tt>';
- # see {Proxy Using ENV['http_proxy']}[rdoc-ref:Net::HTTP@Proxy+Using+-27ENV-5B-27http_proxy-27-5D-27].
+ # see {Proxy Using ENV['http_proxy']}[rdoc-ref:Net::HTTP@Proxy+Using+ENVHTTPProxy].
attr_writer :proxy_from_env
# Sets the proxy address;
@@ -1252,6 +1323,10 @@ module Net #:nodoc:
# see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
attr_writer :proxy_pass
+ # Sets whether the proxy uses SSL;
+ # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
+ attr_writer :proxy_use_ssl
+
# Returns the IP address for the connection.
#
# If the session has not been started,
@@ -1440,23 +1515,6 @@ module Net #:nodoc:
@use_ssl = flag
end
- SSL_IVNAMES = [
- :@ca_file,
- :@ca_path,
- :@cert,
- :@cert_store,
- :@ciphers,
- :@extra_chain_cert,
- :@key,
- :@ssl_timeout,
- :@ssl_version,
- :@min_version,
- :@max_version,
- :@verify_callback,
- :@verify_depth,
- :@verify_mode,
- :@verify_hostname,
- ] # :nodoc:
SSL_ATTRIBUTES = [
:ca_file,
:ca_path,
@@ -1473,7 +1531,9 @@ module Net #:nodoc:
:verify_depth,
:verify_mode,
:verify_hostname,
- ] # :nodoc:
+ ].freeze # :nodoc:
+
+ SSL_IVNAMES = SSL_ATTRIBUTES.map { |a| "@#{a}".to_sym }.freeze # :nodoc:
# Sets or returns the path to a CA certification file in PEM format.
attr_accessor :ca_file
@@ -1490,11 +1550,11 @@ module Net #:nodoc:
attr_accessor :cert_store
# Sets or returns the available SSL ciphers.
- # See {OpenSSL::SSL::SSLContext#ciphers=}[rdoc-ref:OpenSSL::SSL::SSLContext#ciphers-3D].
+ # See {OpenSSL::SSL::SSLContext#ciphers=}[OpenSSL::SSL::SSL::Context#ciphers=].
attr_accessor :ciphers
# Sets or returns the extra X509 certificates to be added to the certificate chain.
- # See {OpenSSL::SSL::SSLContext#add_certificate}[rdoc-ref:OpenSSL::SSL::SSLContext#add_certificate].
+ # See {OpenSSL::SSL::SSLContext#add_certificate}[OpenSSL::SSL::SSL::Context#add_certificate].
attr_accessor :extra_chain_cert
# Sets or returns the OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object.
@@ -1504,15 +1564,15 @@ module Net #:nodoc:
attr_accessor :ssl_timeout
# Sets or returns the SSL version.
- # See {OpenSSL::SSL::SSLContext#ssl_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#ssl_version-3D].
+ # See {OpenSSL::SSL::SSLContext#ssl_version=}[OpenSSL::SSL::SSL::Context#ssl_version=].
attr_accessor :ssl_version
# Sets or returns the minimum SSL version.
- # See {OpenSSL::SSL::SSLContext#min_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#min_version-3D].
+ # See {OpenSSL::SSL::SSLContext#min_version=}[OpenSSL::SSL::SSL::Context#min_version=].
attr_accessor :min_version
# Sets or returns the maximum SSL version.
- # See {OpenSSL::SSL::SSLContext#max_version=}[rdoc-ref:OpenSSL::SSL::SSLContext#max_version-3D].
+ # See {OpenSSL::SSL::SSLContext#max_version=}[OpenSSL::SSL::SSL::Context#max_version=].
attr_accessor :max_version
# Sets or returns the callback for the server certification verification.
@@ -1528,7 +1588,7 @@ module Net #:nodoc:
# Sets or returns whether to verify that the server certificate is valid
# for the hostname.
- # See {OpenSSL::SSL::SSLContext#verify_hostname=}[rdoc-ref:OpenSSL::SSL::SSLContext#attribute-i-verify_mode].
+ # See {OpenSSL::SSL::SSLContext#verify_hostname=}[OpenSSL::SSL::SSL::Context#verify_hostname=].
attr_accessor :verify_hostname
# Returns the X509 certificate chain (an array of strings)
@@ -1576,6 +1636,21 @@ module Net #:nodoc:
self
end
+ # Finishes the \HTTP session:
+ #
+ # http = Net::HTTP.new(hostname)
+ # http.start
+ # http.started? # => true
+ # http.finish # => nil
+ # http.started? # => false
+ #
+ # Raises IOError if not in a session.
+ def finish
+ raise IOError, 'HTTP session not yet started' unless started?
+ do_finish
+ end
+
+ # :stopdoc:
def do_start
connect
@started = true
@@ -1598,19 +1673,26 @@ module Net #:nodoc:
end
debug "opening connection to #{conn_addr}:#{conn_port}..."
- s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
- begin
- TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
- rescue => e
- raise e, "Failed to open TCP connection to " +
- "#{conn_addr}:#{conn_port} (#{e.message})"
+ begin
+ s = timeouted_connect(conn_addr, conn_port)
+ rescue => e
+ if (defined?(IO::TimeoutError) && e.is_a?(IO::TimeoutError)) || e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions
+ e = Net::OpenTimeout.new(e)
end
- }
+ raise e, "Failed to open TCP connection to " +
+ "#{conn_addr}:#{conn_port} (#{e.message})"
+ end
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
debug "opened"
if use_ssl?
if proxy?
- plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
+ if @proxy_use_ssl
+ proxy_sock = OpenSSL::SSL::SSLSocket.new(s)
+ ssl_socket_connect(proxy_sock, @open_timeout)
+ else
+ proxy_sock = s
+ end
+ proxy_sock = BufferedIO.new(proxy_sock, read_timeout: @read_timeout,
write_timeout: @write_timeout,
continue_timeout: @continue_timeout,
debug_output: @debug_output)
@@ -1621,8 +1703,8 @@ module Net #:nodoc:
buf << "Proxy-Authorization: Basic #{credential}\r\n"
end
buf << "\r\n"
- plain_sock.write(buf)
- HTTPResponse.read_new(plain_sock).value
+ proxy_sock.write(buf)
+ HTTPResponse.read_new(proxy_sock).value
# assuming nothing left in buffers after successful CONNECT response
end
@@ -1692,23 +1774,30 @@ module Net #:nodoc:
end
private :connect
- def on_connect
+ tcp_socket_parameters = TCPSocket.instance_method(:initialize).parameters
+ TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT = if tcp_socket_parameters != [[:rest]]
+ tcp_socket_parameters.include?([:key, :open_timeout])
+ else
+ # Use Socket.tcp to find out since there is no parameters information for TCPSocket#initialize
+ # See discussion in https://github.com/ruby/net-http/pull/224
+ Socket.method(:tcp).parameters.include?([:key, :open_timeout])
end
- private :on_connect
+ private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT
- # Finishes the \HTTP session:
- #
- # http = Net::HTTP.new(hostname)
- # http.start
- # http.started? # => true
- # http.finish # => nil
- # http.started? # => false
- #
- # Raises IOError if not in a session.
- def finish
- raise IOError, 'HTTP session not yet started' unless started?
- do_finish
+ def timeouted_connect(conn_addr, conn_port)
+ if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout)
+ else
+ Timeout.timeout(@open_timeout, Net::OpenTimeout) {
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
+ }
+ end
+ end
+ private :timeouted_connect
+
+ def on_connect
end
+ private :on_connect
def do_finish
@started = false
@@ -1730,13 +1819,14 @@ module Net #:nodoc:
@proxy_port = nil
@proxy_user = nil
@proxy_pass = nil
+ @proxy_use_ssl = nil
# Creates an \HTTP proxy class which behaves like \Net::HTTP, but
# performs all access via the specified proxy.
#
# This class is obsolete. You may pass these same parameters directly to
# \Net::HTTP.new. See Net::HTTP.new for details of the arguments.
- def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
+ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ssl = nil) #:nodoc:
return self unless p_addr
Class.new(self) {
@@ -1754,9 +1844,12 @@ module Net #:nodoc:
@proxy_user = p_user
@proxy_pass = p_pass
+ @proxy_use_ssl = p_use_ssl
}
end
+ # :startdoc:
+
class << HTTP
# Returns true if self is a class which was created by HTTP::Proxy.
def proxy_class?
@@ -1778,6 +1871,9 @@ module Net #:nodoc:
# Returns the password for accessing the proxy, or +nil+ if none;
# see Net::HTTP@Proxy+Server.
attr_reader :proxy_pass
+
+ # Use SSL when talking to the proxy. If Net::HTTP does not use a proxy, nil.
+ attr_reader :proxy_use_ssl
end
# Returns +true+ if a proxy server is defined, +false+ otherwise;
@@ -1848,9 +1944,11 @@ module Net #:nodoc:
alias proxyport proxy_port #:nodoc: obsolete
private
+ # :stopdoc:
def unescape(value)
- require 'cgi/util'
+ require 'cgi/escape'
+ require 'cgi/util' unless defined?(CGI::EscapeExt)
CGI.unescape(value)
end
@@ -1875,6 +1973,7 @@ module Net #:nodoc:
path
end
end
+ # :startdoc:
#
# HTTP operations
@@ -2012,6 +2111,11 @@ module Net #:nodoc:
# http = Net::HTTP.new(hostname)
# http.put('/todos/1', data) # => #<Net::HTTPOK 200 OK readbody=true>
#
+ # Related:
+ #
+ # - Net::HTTP::Put: request class for \HTTP method PUT.
+ # - Net::HTTP.put: sends PUT request, returns response body.
+ #
def put(path, data, initheader = nil)
request(Put.new(path, initheader), data)
end
@@ -2324,7 +2428,9 @@ module Net #:nodoc:
res
end
- IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc:
+ # :stopdoc:
+
+ IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/.freeze # :nodoc:
def transport_request(req)
count = 0
@@ -2350,7 +2456,10 @@ module Net #:nodoc:
res
}
res.reading_body(@socket, req.response_body_permitted?) {
- yield res if block_given?
+ if block_given?
+ count = max_retries # Don't restart in the middle of a download
+ yield res
+ end
}
rescue Net::OpenTimeout
raise
@@ -2478,6 +2587,11 @@ module Net #:nodoc:
alias_method :D, :debug
end
+ # for backward compatibility until Ruby 4.0
+ # https://bugs.ruby-lang.org/issues/20900
+ # https://github.com/bblimke/webmock/pull/1081
+ HTTPSession = HTTP
+ deprecate_constant :HTTPSession
end
require_relative 'http/exceptions'
@@ -2492,5 +2606,3 @@ require_relative 'http/response'
require_relative 'http/responses'
require_relative 'http/proxy_delta'
-
-require_relative 'http/backward'