diff --git a/lib/net/http.rb b/lib/net/http.rb index 7fd4c3ed..44d31472 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -761,11 +761,23 @@ class << HTTP # Like Net::HTTP.get, but writes the returned body to $stdout; # returns +nil+. def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) - get_response(uri_or_host, path_or_headers, port) {|res| - res.read_body do |chunk| - $stdout.print chunk - end - } + if path_or_headers && !path_or_headers.is_a?(Hash) + host = uri_or_host #: String + path = path_or_headers #: String + get_response(host, path, port) {|res| + res.read_body do |chunk| + $stdout.print chunk + end + } + else + uri = uri_or_host #: URI::HTTP + headers = path_or_headers #: headers? + get_response(uri, headers) {|res| + res.read_body do |chunk| + $stdout.print chunk + end + } + end nil end @@ -802,7 +814,15 @@ def HTTP.get_print(uri_or_host, path_or_headers = nil, port = nil) # - Net::HTTP#get: convenience method for \HTTP method +GET+. # def HTTP.get(uri_or_host, path_or_headers = nil, port = nil) - get_response(uri_or_host, path_or_headers, port).body + if path_or_headers && !path_or_headers.is_a?(Hash) + host = uri_or_host #: String + path = path_or_headers #: String + get_response(host, path, port).body + else + uri = uri_or_host #: URI::HTTP + headers = path_or_headers #: headers? + get_response(uri, headers).body + end end # :call-seq: @@ -813,15 +833,16 @@ def HTTP.get(uri_or_host, path_or_headers = nil, port = nil) # instead of the body string. def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block) if path_or_headers && !path_or_headers.is_a?(Hash) - host = uri_or_host - path = path_or_headers + host = uri_or_host #: String + path = path_or_headers #: String new(host, port || HTTP.default_port).start {|http| return http.request_get(path, &block) } else - uri = uri_or_host - headers = path_or_headers - start(uri.hostname, uri.port, + uri = uri_or_host #: URI::HTTP + headers = path_or_headers #: headers? + hostname = uri.hostname or raise ArgumentError, "no host component for URI" + start(hostname, uri.port, :use_ssl => uri.scheme == 'https') {|http| return http.request_get(uri, headers, &block) } @@ -855,7 +876,8 @@ def HTTP.get_response(uri_or_host, path_or_headers = nil, port = nil, &block) # - Net::HTTP#post: convenience method for \HTTP method +POST+. # def HTTP.post(url, data, header = nil) - start(url.hostname, url.port, + hostname = url.hostname or raise ArgumentError, "no host component for URI" + start(hostname, url.port, :use_ssl => url.scheme == 'https' ) {|http| http.post(url, data, header) } @@ -884,8 +906,11 @@ def HTTP.post(url, data, header = nil) def HTTP.post_form(url, params) req = Post.new(url) req.form_data = params - req.basic_auth url.user, url.password if url.user - start(url.hostname, url.port, + if user = url.user + req.basic_auth(user, url.password.to_s) + end + hostname = url.hostname or raise ArgumentError, "no host component for URI" + start(hostname, url.port, :use_ssl => url.scheme == 'https' ) {|http| http.request(req) } @@ -918,7 +943,8 @@ def HTTP.post_form(url, params) # - Net::HTTP#put: convenience method for \HTTP method +PUT+. # def HTTP.put(url, data, header = nil) - start(url.hostname, url.port, + hostname = url.hostname or raise ArgumentError, "no host component for URI" + start(hostname, url.port, :use_ssl => url.scheme == 'https' ) {|http| http.put(url, data, header) } @@ -1054,14 +1080,22 @@ def HTTP.start(address, *arg, &block) # :yield: +http+ if opt[:use_ssl] opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt) end - http.methods.grep(/\A(\w+)=\z/) do |meth| - key = $1.to_sym - opt.key?(key) or next + http.methods.grep(/\A(\w+)=\z/).each do |meth| + match = /\A(\w+)=\z/.match(meth.to_s) + next unless match + key = match[1] + next unless key + key = key.to_sym + next unless opt.key?(key) http.__send__(meth, opt[key]) end end - http.start(&block) + if block + http.start(&block) + else + http.start + end end class << HTTP @@ -1098,7 +1132,7 @@ class << HTTP # 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, p_use_ssl = nil) - http = super address, port + http = newobj(address, port) if proxy_class? then # from Net::HTTP::Proxy() http.proxy_from_env = @proxy_from_env @@ -1107,14 +1141,14 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p http.proxy_user = @proxy_user http.proxy_pass = @proxy_pass http.proxy_use_ssl = @proxy_use_ssl - elsif p_addr == :ENV then + elsif p_addr.is_a?(Symbol) && p_addr == :ENV then http.proxy_from_env = true else - if p_addr && p_no_proxy && !URI::Generic.use_proxy?(address, address, port, p_no_proxy) + if p_addr && p_no_proxy && !URI::Generic.use_proxy?(address, address, port || default_port, p_no_proxy) p_addr = nil p_port = nil end - http.proxy_address = p_addr + http.proxy_address = p_addr&.to_s http.proxy_port = p_port || default_port http.proxy_user = p_user http.proxy_pass = p_pass @@ -1347,7 +1381,12 @@ def response_body_encoding=(value) # http.finish # def ipaddr - started? ? @socket.io.peeraddr[3] : @ipaddr + if started? + socket = @socket or return @ipaddr + socket.io.peeraddr[3] + else + @ipaddr + end end # Sets the IP address for the connection: @@ -1416,7 +1455,9 @@ def max_retries=(retries) # http.get('/todos/1') # Raises Net::ReadTimeout. # def read_timeout=(sec) - @socket.read_timeout = sec if @socket + if (socket = @socket) + socket.read_timeout = sec + end @read_timeout = sec end @@ -1440,7 +1481,9 @@ def read_timeout=(sec) # http.post(_uri.path, data, headers) # Raises Net::WriteTimeout. # def write_timeout=(sec) - @socket.write_timeout = sec if @socket + if (socket = @socket) + socket.write_timeout = sec + end @write_timeout = sec end @@ -1453,7 +1496,9 @@ def write_timeout=(sec) # If the \HTTP object does not receive a response in this many seconds # it sends the request body. def continue_timeout=(sec) - @socket.continue_timeout = sec if @socket + if (socket = @socket) + socket.continue_timeout = sec + end @continue_timeout = sec end @@ -1595,10 +1640,11 @@ def use_ssl=(flag) # for the session's socket peer, # or +nil+ if none. def peer_cert - if not use_ssl? or not @socket + socket = @socket + if !use_ssl? || !socket return nil end - @socket.io.peer_cert + socket.io.peer_cert end # Starts an \HTTP session. @@ -1676,7 +1722,8 @@ def connect 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 + timeout_error = IO.const_get(:TimeoutError, false) if IO.const_defined?(:TimeoutError, false) + if (timeout_error && e.is_a?(timeout_error)) || e.is_a?(Errno::ETIMEDOUT) # for compatibility with previous versions e = Net::OpenTimeout.new(e) end raise e, "Failed to open TCP connection to " + @@ -1718,19 +1765,23 @@ def connect end end end - @ssl_context.set_params(ssl_parameters) - unless @ssl_context.session_cache_mode.nil? # a dummy method on JRuby - @ssl_context.session_cache_mode = + ssl_context = @ssl_context or raise OpenSSL::SSL::SSLError, 'SSL context not initialized' + ssl_context.set_params(ssl_parameters) + unless ssl_context.session_cache_mode.nil? # a dummy method on JRuby + ssl_context.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT | OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE end - if @ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby - @ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess } + if ssl_context.respond_to?(:session_new_cb) # not implemented under JRuby + ssl_context.session_new_cb = ->(socket_or_pair) do + socket, session = Array(socket_or_pair) + @ssl_session = session || socket.session + end end # Still do the post_connection_check below even if connecting # to IP address - verify_hostname = @ssl_context.verify_hostname + verify_hostname = ssl_context.verify_hostname # Server Name Indication (SNI) RFC 3546/6066 case @address @@ -1739,25 +1790,26 @@ def connect # per RFC 6066, section 3. # Avoid openssl warning - @ssl_context.verify_hostname = false + ssl_context.verify_hostname = false else ssl_host_address = @address end debug "starting SSL for #{conn_addr}:#{conn_port}..." - s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) + s = OpenSSL::SSL::SSLSocket.new(s, ssl_context) s.sync_close = true s.hostname = ssl_host_address if s.respond_to?(:hostname=) && ssl_host_address - if @ssl_session and - Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout - s.session = @ssl_session + if (ssl_session = @ssl_session) && + Process.clock_gettime(Process::CLOCK_REALTIME) < ssl_session.time.to_f + ssl_session.timeout + s.session = ssl_session end ssl_socket_connect(s, @open_timeout) - if (@ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname + if (ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE) && verify_hostname s.post_connection_check(@address) end - debug "SSL established, protocol: #{s.ssl_version}, cipher: #{s.cipher[0]}" + cipher = s.cipher + debug "SSL established, protocol: #{s.ssl_version}, cipher: #{cipher ? cipher[0] : nil}" end @socket = BufferedIO.new(s, read_timeout: @read_timeout, write_timeout: @write_timeout, @@ -1785,11 +1837,13 @@ def connect private_constant :TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT def timeouted_connect(conn_addr, conn_port) + conn_addr = conn_addr or raise ArgumentError, "missing connection address" + conn_port = conn_port or raise ArgumentError, "missing connection port" if TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT - TCPSocket.open(conn_addr, conn_port, @local_host, @local_port, open_timeout: @open_timeout) + TCPSocket.new(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) + TCPSocket.new(conn_addr, conn_port, @local_host, @local_port) } end end @@ -1801,7 +1855,9 @@ def on_connect def do_finish @started = false - @socket.close if @socket + if (socket = @socket) + socket.close + end @socket = nil end private :do_finish @@ -1832,13 +1888,14 @@ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ss Class.new(self) { @is_proxy_class = true - if p_addr == :ENV then + case p_addr + when :ENV @proxy_from_env = true @proxy_address = nil @proxy_port = nil else @proxy_from_env = false - @proxy_address = p_addr + @proxy_address = String(p_addr) @proxy_port = p_port || default_port end @@ -2011,13 +2068,14 @@ def edit_path(path) # - Net::HTTP.get: sends GET request, returns response body. # def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+ - res = nil - request(Get.new(path, initheader)) {|r| - r.read_body dest, &block - res = r + if block + raise ArgumentError, 'both arg and block given for HTTP method' if dest + r.read_body(&block) + else + r.read_body(dest) + end } - res end # Sends a HEAD request to the server; @@ -2404,7 +2462,7 @@ def request(req, body = nil, &block) # :yield: +response+ } end if proxy_user() - req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl? + req.proxy_basic_auth proxy_user(), proxy_pass().to_s unless use_ssl? end req.set_body_internal body res = transport_request(req, &block) @@ -2420,12 +2478,14 @@ def request(req, body = nil, &block) # :yield: +response+ # Executes a request which uses a representation # and returns its body. def send_entity(path, data, initheader, dest, type, &block) - res = nil request(type.new(path, initheader), data) {|r| - r.read_body dest, &block - res = r + if block + raise ArgumentError, 'both arg and block given for HTTP method' if dest + r.read_body(&block) + else + r.read_body(dest) + end } - res end # :stopdoc: @@ -2437,24 +2497,29 @@ def transport_request(req) begin begin_transport req res = catch(:response) { + socket = @socket or raise IOError, 'HTTP session not yet started' begin - req.exec @socket, @curr_http_version, edit_path(req.path) + req.exec socket, @curr_http_version, edit_path(req.path) rescue Errno::EPIPE # Failure when writing full request, but we can probably # still read the received response. end - begin - res = HTTPResponse.read_new(@socket) + res = HTTPResponse.read_new(socket) + loop do res.decode_content = req.decode_content res.body_encoding = @response_body_encoding res.ignore_eof = @ignore_eof - end while res.kind_of?(HTTPInformation) + break unless res.kind_of?(HTTPInformation) + res = HTTPResponse.read_new(socket) + end - res.uri = req.uri + response = res or raise Net::HTTPBadResponse, 'no HTTP response' + response.uri = req.uri if req.uri - res + response } + res = res or raise Net::HTTPBadResponse, 'no HTTP response' res.reading_body(@socket, req.response_body_permitted?) { if block_given? count = max_retries # Don't restart in the middle of a download @@ -2470,12 +2535,16 @@ def transport_request(req) Timeout::Error => exception if count < max_retries && IDEMPOTENT_METHODS_.include?(req.method) count += 1 - @socket.close if @socket + if (socket = @socket) + socket.close + end debug "Conn close because of error #{exception}, and retry" retry end debug "Conn close because of error #{exception}" - @socket.close if @socket + if (socket = @socket) + socket.close + end raise end @@ -2483,21 +2552,25 @@ def transport_request(req) res rescue => exception debug "Conn close because of error #{exception}" - @socket.close if @socket + if (socket = @socket) + socket.close + end raise exception end def begin_transport(req) - if @socket.closed? + socket = @socket or raise IOError, 'HTTP session not yet started' + if socket.closed? connect elsif @last_communicated - if @last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC) + last_communicated = @last_communicated + if last_communicated && last_communicated + @keep_alive_timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC) debug 'Conn close because of keep_alive_timeout' - @socket.close + socket.close connect - elsif @socket.io.to_io.wait_readable(0) && @socket.eof? + elsif socket.io.to_io.wait_readable(0) && socket.eof? debug "Conn close because of EOF" - @socket.close + socket.close connect end end @@ -2511,19 +2584,20 @@ def begin_transport(req) end def end_transport(req, res) - @curr_http_version = res.http_version + @curr_http_version = res.http_version || HTTPVersion @last_communicated = nil - if @socket.closed? + socket = @socket or raise IOError, 'HTTP session not yet started' + if socket.closed? debug 'Conn socket closed' elsif not res.body and @close_on_empty_response debug 'Conn close' - @socket.close + socket.close elsif keep_alive?(req, res) debug 'Conn keep-alive' @last_communicated = Process.clock_gettime(Process::CLOCK_MONOTONIC) else debug 'Conn close' - @socket.close + socket.close end end @@ -2539,7 +2613,7 @@ def keep_alive?(req, res) def sspi_auth?(res) return false unless @sspi_enabled if res.kind_of?(HTTPProxyAuthenticationRequired) and - proxy? and res["Proxy-Authenticate"].include?("Negotiate") + proxy? and res["Proxy-Authenticate"].to_s.include?("Negotiate") begin require 'win32/sspi' true @@ -2579,9 +2653,9 @@ def addr_port # Adds a message to debugging output def debug(msg) - return unless @debug_output - @debug_output << msg - @debug_output << "\n" + output = @debug_output or return + output << msg + output << "\n" end alias_method :D, :debug diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb index 5b01ea4a..792cef7e 100644 --- a/lib/net/http/generic_request.rb +++ b/lib/net/http/generic_request.rb @@ -14,8 +14,8 @@ class Net::HTTPGenericRequest def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: @method = m - @request_has_body = reqbody - @response_has_body = resbody + @request_has_body = reqbody ? true : false + @response_has_body = resbody ? true : false if URI === uri_or_path then raise ArgumentError, "not an HTTP URI" unless URI::HTTP === uri_or_path @@ -36,10 +36,10 @@ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: if Net::HTTP::HAVE_ZLIB then if !initheader || !initheader.keys.any? { |k| - %w[accept-encoding range].include? k.downcase + %w[accept-encoding range].include? k.to_s.downcase } then @decode_content = true if @response_has_body - initheader = initheader ? initheader.dup : {} + initheader = initheader ? initheader.dup : {} #: Hash[String | Symbol, untyped] initheader["accept-encoding"] = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" end @@ -48,7 +48,9 @@ def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) # :nodoc: initialize_http_header initheader self['Accept'] ||= '*/*' self['User-Agent'] ||= 'Ruby' - self['Host'] ||= @uri.authority if @uri + if (uri = @uri) + self['Host'] ||= uri.authority + end @body = nil @body_stream = nil @body_data = nil @@ -129,7 +131,7 @@ def pretty_print(q) # they want to handle it. def []=(key, val) # :nodoc: - @decode_content = false if key.downcase == 'accept-encoding' + @decode_content = false if key.to_s.downcase == 'accept-encoding' super key, val end @@ -218,12 +220,12 @@ def set_body_internal(str) #:nodoc: internal use only # 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 + if body = @body + send_request_with_body sock, ver, path, body + elsif body_stream = @body_stream + send_request_with_body_stream sock, ver, path, body_stream + elsif body_data = @body_data + send_request_with_body_data sock, ver, path, body_data else write_header sock, ver, path end @@ -231,7 +233,7 @@ def exec(sock, ver, path) #:nodoc: internal use only def update_uri(addr, port, ssl) # :nodoc: internal use only # reflect the connection and @path to @uri - return unless @uri + uri = @uri or return if ssl scheme = 'https' @@ -243,19 +245,19 @@ def update_uri(addr, port, ssl) # :nodoc: internal use only if host = self['host'] host = URI.parse("//#{host}").host # Remove a port component from the existing Host header - elsif host = @uri.host + 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 + if uri.is_a?(klass) + uri.host = host + uri.port = port else @uri = klass.new( - scheme, @uri.userinfo, + scheme, uri.userinfo, host, port, nil, - @uri.path, nil, @uri.query, nil) + uri.path, nil, uri.query, nil) end end @@ -266,13 +268,13 @@ def update_uri(addr, port, ssl) # :nodoc: internal use only class Chunker #:nodoc: def initialize(sock) @sock = sock - @prev = nil end - def write(buf) + 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") + chunk = buf.join + rv = chunk.bytesize + @sock.write("#{rv.to_s(16)}\r\n", chunk, "\r\n") rv end @@ -314,7 +316,7 @@ def send_request_with_body_data(sock, ver, path, params) 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]) + self.set_content_type(self.content_type.to_s, boundary: opt[:boundary]) if chunked? write_header sock, ver, path encode_multipart_form_data(sock, params, opt) diff --git a/lib/net/http/header.rb b/lib/net/http/header.rb index 797a3bea..abfa4cd3 100644 --- a/lib/net/http/header.rb +++ b/lib/net/http/header.rb @@ -185,7 +185,7 @@ module Net::HTTPHeader MAX_FIELD_LENGTH = 65536 def initialize_http_header(initheader) #:nodoc: - @header = {} + @header = {} #: Hash[String, Array[String]] return unless initheader initheader.each do |key, value| warn "net/http: duplicated HTTP header: #{key}", uplevel: 3 if key?(key) and $VERBOSE @@ -202,7 +202,7 @@ def initialize_http_header(initheader) #:nodoc: 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] + @header[key.to_s.downcase] = [value] end end end @@ -224,7 +224,7 @@ def size #:nodoc: obsolete # Note that some field values may be retrieved via convenience methods; # see {Getters}[rdoc-ref:Net::HTTPHeader@Getters]. def [](key) - a = @header[key.downcase.to_s] or return nil + a = @header[String(key).downcase] or return nil a.join(', ') end @@ -241,7 +241,7 @@ def [](key) # see {Setters}[rdoc-ref:Net::HTTPHeader@Setters]. def []=(key, val) unless val - @header.delete key.downcase.to_s + @header.delete String(key).downcase return val end set_field(key, val) @@ -261,7 +261,7 @@ def []=(key, val) # req.get_fields('Foo') # => ["bar", "baz", "baz", "bam"] # def add_field(key, val) - stringified_downcased_key = key.downcase.to_s + stringified_downcased_key = String(key).downcase if @header.key?(stringified_downcased_key) append_field_value(@header[stringified_downcased_key], val) else @@ -273,15 +273,15 @@ def add_field(key, val) private def set_field(key, val) case val when Enumerable - ary = [] + ary = [] #: Array[String] append_field_value(ary, val) - @header[key.downcase.to_s] = ary + @header[String(key).downcase] = 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] + @header[String(key).downcase] = [val] end end @@ -308,7 +308,7 @@ def add_field(key, val) # res.get_fields('Nosuch') # => nil # def get_fields(key) - stringified_downcased_key = key.downcase.to_s + stringified_downcased_key = String(key).downcase return nil unless @header[stringified_downcased_key] @header[stringified_downcased_key].dup end @@ -343,7 +343,17 @@ def get_fields(key) # res.fetch('Nosuch') # Raises KeyError. # def fetch(key, *args, &block) #:yield: +key+ - a = @header.fetch(key.downcase.to_s, *args, &block) + key = String(key).downcase + a = if (value = @header[key]) + value + elsif block + handler = block + return handler.call(key) + elsif args.empty? + @header.fetch(key) + else + return args.first + end a.kind_of?(Array) ? a.join(', ') : a end @@ -366,7 +376,7 @@ def fetch(key, *args, &block) #:yield: +key+ # # Net::HTTPHeader#each is an alias for Net::HTTPHeader#each_header. def each_header #:yield: +key+, +value+ - block_given? or return enum_for(__method__) { @header.size } + block_given? or return enum_for(:each_header) { @header.size } @header.each do |k,va| yield k, va.join(', ') end @@ -393,8 +403,11 @@ def each_header #:yield: +key+, +value+ # # Net::HTTPHeader#each_name is an alias for Net::HTTPHeader#each_key. def each_name(&block) #:yield: +key+ - block_given? or return enum_for(__method__) { @header.size } - @header.each_key(&block) + block_given? or return enum_for(:each_name) { @header.size } + handler = block or return enum_for(:each_name) { @header.size } + @header.each_key do |key| + handler.call(key) + end end alias each_key each_name @@ -419,7 +432,7 @@ def each_name(&block) #:yield: +key+ # # Returns an enumerator if no block is given. def each_capitalized_name #:yield: +key+ - block_given? or return enum_for(__method__) { @header.size } + block_given? or return enum_for(:each_capitalized_name) { @header.size } @header.each_key do |k| yield capitalize(k) end @@ -440,7 +453,7 @@ def each_capitalized_name #:yield: +key+ # # Returns an enumerator if no block is given. def each_value #:yield: +value+ - block_given? or return enum_for(__method__) { @header.size } + block_given? or return enum_for(:each_value) { @header.size } @header.each_value do |va| yield va.join(', ') end @@ -455,7 +468,7 @@ def each_value #:yield: +value+ # req.delete('Nosuch') # => nil # def delete(key) - @header.delete(key.downcase.to_s) + @header.delete(String(key).downcase) end # Returns +true+ if the field for the case-insensitive +key+ exists, +false+ otherwise: @@ -465,7 +478,7 @@ def delete(key) # req.key?('Nosuch') # => false # def key?(key) - @header.key?(key.downcase.to_s) + @header.key?(String(key).downcase) end # Returns a hash of the key/value pairs: @@ -486,7 +499,7 @@ def to_hash # # Net::HTTPHeader#canonical_each is an alias for Net::HTTPHeader#each_capitalized. def each_capitalized - block_given? or return enum_for(__method__) { @header.size } + block_given? or return enum_for(:each_capitalized) { @header.size } @header.each do |k,v| yield capitalize(k), v.join(', ') end @@ -524,7 +537,7 @@ def range raise Net::HTTPHeaderSyntaxError, "invalid syntax for byte-ranges-specifier: '#{value}'" end - byte_range_set = $1 + byte_range_set = $1.to_s result = byte_range_set.split(/,/).map {|spec| m = /(\d+)?\s*-\s*(\d+)?/i.match(spec) or raise Net::HTTPHeaderSyntaxError, "invalid byte-range-spec: '#{spec}'" @@ -582,10 +595,14 @@ def set_range(r, e = nil) @header.delete 'range' return r end - r = (r...r+e) if e + if e + first = Integer(r) or raise TypeError, 'Range/Integer is required' + length = Integer(e) or raise TypeError, 'Range/Integer is required' + r = first...(first + length) + end case r when Numeric - n = r.to_i + n = Integer(r) rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}") when Range first = r.first @@ -619,7 +636,7 @@ def set_range(r, e = nil) # def content_length return nil unless key?('Content-Length') - len = self['Content-Length'].slice(/\d+/) or + len = self['Content-Length'].to_s.slice(/\d+/) or raise Net::HTTPHeaderSyntaxError, 'wrong Content-Length format' len.to_i end @@ -726,7 +743,7 @@ def content_type # def main_type return nil unless @header['content-type'] - self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip + self['Content-Type'].to_s.split(';').first.to_s.split('/')[0].to_s.strip end # Returns the trailing ('subtype') part of the @@ -741,7 +758,7 @@ def main_type # def sub_type return nil unless @header['content-type'] - _, sub = *self['Content-Type'].split(';').first.to_s.split('/') + _, sub = *self['Content-Type'].to_s.split(';').first.to_s.split('/') return nil unless sub sub.strip end @@ -755,11 +772,12 @@ def sub_type # res.type_params # => {"charset"=>"utf-8"} # def type_params - result = {} + result = {} #: Hash[String, String] list = self['Content-Type'].to_s.split(';') list.shift list.each do |param| k, v = *param.split('=', 2) + next unless k && v result[k.strip] = v.strip end result @@ -774,7 +792,7 @@ def type_params # # Net::HTTPHeader#content_type= is an alias for Net::HTTPHeader#set_content_type. def set_content_type(type, params = {}) - @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')] + @header['content-type'] = [type.to_s + params.map{|k,v|"; #{k}=#{v}"}.join('')] end alias content_type= set_content_type diff --git a/lib/net/http/response.rb b/lib/net/http/response.rb index bea4346e..c2424860 100644 --- a/lib/net/http/response.rb +++ b/lib/net/http/response.rb @@ -159,7 +159,8 @@ def read_status_line(sock) str = sock.readline m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?\z/in.match(str) or raise Net::HTTPBadResponse, "wrong status line: #{str.dump}" - m.captures + code = m[2] or raise Net::HTTPBadResponse, "wrong status line: #{str.dump}" + [m[1], code, m[3]] end def response_class(code) @@ -169,20 +170,30 @@ def response_class(code) end def each_response_header(sock) - key = value = nil + key = nil #: String? + value = nil #: String? 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 + if (line[0] == ?\s || line[0] == ?\t) && value.is_a?(String) + current_value = value + current_value << ' ' unless current_value.empty? + current_value << line.strip else - yield key, value if key + if key.is_a?(String) && value.is_a?(String) + current_key = key + current_value = value + yield current_key, current_value + end key, value = line.strip.split(/\s*:\s*/, 2) raise Net::HTTPBadResponse, 'wrong header line format' if value.nil? end end - yield key, value if key + if key.is_a?(String) && value.is_a?(String) + current_key = key + current_value = value + yield current_key, current_value + end end end @@ -274,7 +285,9 @@ def code_type #:nodoc: def error! #:nodoc: message = @code - message = "#{message} #{@message.dump}" if @message + if detail = @message + message = "#{message} #{detail.dump}" + end raise error_type().new(message, self) end @@ -369,16 +382,15 @@ def read_body(dest = nil, &block) @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) + enc = @body_encoding + if enc.nil? || enc == false + # nil/false: do not force encoding + elsif !enc.is_a?(Encoding) + body = @body or return + enc = detect_encoding(body) end - @body.force_encoding(enc) if enc + @body&.force_encoding(enc) if enc @body end @@ -466,10 +478,10 @@ def scanning_meta(str) require 'strscan' ss = StringScanner.new(str) if ss.scan_until(/]*/) + name = ss.scan(/[^=\t\n\f\r \/>]*/).to_s name.downcase! raise if name.empty? ss.skip(/[\t\n\f\r ]*/) @@ -521,18 +539,18 @@ def get_attribute(ss) case ss.peek(1) when '"' ss.getch - value = ss.scan(/[^"]+/) + value = ss.scan(/[^"]+/).to_s value.downcase! ss.getch when "'" ss.getch - value = ss.scan(/[^']+/) + value = ss.scan(/[^']+/).to_s value.downcase! ss.getch when '>' value = '' else - value = ss.scan(/[^\t\n\f\r >]+/) + value = ss.scan(/[^\t\n\f\r >]+/).to_s value.downcase! end [name, value] @@ -556,16 +574,18 @@ def extracting_encodings_from_meta_elements(value) # bytes in the range may not be a complete deflate block. def inflater # :nodoc: - return yield @socket unless Net::HTTP::HAVE_ZLIB - return yield @socket unless @decode_content - return yield @socket if self['content-range'] + socket = @socket or raise IOError, 'attempt to read body out of block' + return yield socket unless Net::HTTP::HAVE_ZLIB + return yield socket unless @decode_content + return yield socket if self['content-range'] v = self['content-encoding'] case v&.downcase when 'deflate', 'gzip', 'x-gzip' then self.delete 'content-encoding' - inflate_body_io = Inflater.new(@socket) + inflate_body_io = Inflater.new(socket) + success = false begin yield inflate_body_io @@ -584,9 +604,9 @@ def inflater # :nodoc: when 'none', 'identity' then self.delete 'content-encoding' - yield @socket + yield socket else - yield @socket + yield socket end end @@ -597,19 +617,19 @@ def read_body_0(dest) return end - @socket = inflate_body_io + body_io = inflate_body_io clen = content_length() if clen - @socket.read clen, dest, @ignore_eof + body_io.read clen, dest, @ignore_eof return end clen = range_length() if clen - @socket.read clen, dest + body_io.read clen, dest return end - @socket.read_all dest + body_io.read_all dest end end @@ -621,9 +641,10 @@ def read_body_0(dest) # See RFC 2616 section 3.6.1 for definitions def read_chunked(dest, chunk_data_io) # :nodoc: + socket = @socket or raise IOError, 'attempt to read body out of block' total = 0 while true - line = @socket.readline + line = socket.readline hexlen = line.slice(/[0-9a-fA-F]+/) or raise Net::HTTPBadResponse, "wrong chunk size line: #{line}" len = hexlen.hex @@ -632,16 +653,17 @@ def read_chunked(dest, chunk_data_io) # :nodoc: chunk_data_io.read len, dest ensure total += len - @socket.read 2 # \r\n + socket.read 2 # \r\n end end - until @socket.readline.empty? + until socket.readline.empty? # none end end def stream_check - raise IOError, 'attempt to read body out of block' if @socket.nil? || @socket.closed? + socket = @socket or raise IOError, 'attempt to read body out of block' + raise IOError, 'attempt to read body out of block' if socket.closed? end def procdest(dest, block) @@ -736,4 +758,3 @@ def read_all dest end end - diff --git a/lib/net/http/status.rb b/lib/net/http/status.rb index e70b47d9..2e87f770 100644 --- a/lib/net/http/status.rb +++ b/lib/net/http/status.rb @@ -11,8 +11,10 @@ puts puts "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| + uri = URI(url) #: URI::HTTP + uri.read({}).each_line do |line| code, mes, = line.split(',') + next if mes.nil? next if ['(Unused)', 'Unassigned', 'Description'].include?(mes) puts " #{code} => '#{mes}'," end diff --git a/net-http.gemspec b/net-http.gemspec index d59d5c3b..faa08596 100644 --- a/net-http.gemspec +++ b/net-http.gemspec @@ -30,7 +30,7 @@ Gem::Specification.new do |spec| # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. - excludes = %W[/.git* /bin /test /test_sig /*file /#{File.basename(__FILE__)}] + excludes = %W[/.git* /bin /test /test_sig /sig/*-impl.rbs /*file /#{File.basename(__FILE__)}] spec.files = IO.popen(%W[git -C #{__dir__} ls-files -z --] + excludes.map {|e| ":^#{e}"}, &:read).split("\x0") spec.bindir = "exe" spec.require_paths = ["lib"] diff --git a/sig/net-http-impl.rbs b/sig/net-http-impl.rbs new file mode 100644 index 00000000..30ae6f17 --- /dev/null +++ b/sig/net-http-impl.rbs @@ -0,0 +1,200 @@ +class URI::Generic + def authority: () -> String? +end + +module Win32 + module SSPI + class NegotiateAuth + def self.new: () -> NegotiateAuth + def get_initial_token: () -> String + def complete_authentication: (String authphrase) -> String + end + end +end + +module Net + class HTTPAuthenticationError < StandardError + def initialize: (String msg, untyped err) -> void + end + + module HTTPHeader + @header: Hash[String, Array[String]] + @body: String? + @body_data: Array[untyped] | Hash[untyped, untyped] | nil + @body_stream: untyped + @form_option: Hash[Symbol, untyped] + + attr_accessor body: String? + end + + class HTTP + @address: String + @port: Integer + @ipaddr: String? + @local_host: String? + @local_port: Integer? + @curr_http_version: String + @keep_alive_timeout: Float | Integer + @last_communicated: Float? + @close_on_empty_response: bool + @socket: Net::BufferedIO? + @started: bool + @open_timeout: Float | Integer + @read_timeout: Float | Integer + @write_timeout: Float | Integer + @continue_timeout: Float | Integer | nil + @max_retries: Integer + @debug_output: IO? + @response_body_encoding: Encoding | false | nil + @ignore_eof: bool + @tcpsocket_supports_open_timeout: bool? + @proxy_from_env: bool + @proxy_uri: URI::Generic | false | nil + @proxy_address: String? + @proxy_port: Integer? + @proxy_user: String? + @proxy_pass: String? + @proxy_use_ssl: bool? + @use_ssl: bool + @ssl_context: OpenSSL::SSL::SSLContext? + @ssl_session: OpenSSL::SSL::Session? + @sspi_enabled: bool + self.@is_proxy_class: bool + self.@proxy_from_env: bool + self.@proxy_addr: String? + self.@proxy_address: String? + self.@proxy_port: Integer? + self.@proxy_user: String? + self.@proxy_pass: String? + self.@proxy_use_ssl: bool? + + TCP_SOCKET_NEW_HAS_OPEN_TIMEOUT: bool + IDEMPOTENT_METHODS_: Array[String] + + def initialize: (String address, ?Integer? port) -> void + def do_start: () -> void + def connect: () -> void + def timeouted_connect: (String? conn_addr, Integer? conn_port) -> TCPSocket + def on_connect: () -> void + def do_finish: () -> void + def unescape: (String value) -> String + def conn_address: () -> String? + def conn_port: () -> Integer? + def edit_path: (String path) -> String + def send_entity: (String | URI::HTTP path, String data, headers? initheader, untyped dest, singleton(Net::HTTPRequest) type) ?{ (String body_segment) -> void } -> Net::HTTPResponse + def transport_request: (Net::HTTPGenericRequest req) ?{ (Net::HTTPResponse response) -> void } -> Net::HTTPResponse + def begin_transport: (Net::HTTPGenericRequest req) -> void + def end_transport: (Net::HTTPGenericRequest req, Net::HTTPResponse res) -> void + def keep_alive?: (Net::HTTPGenericRequest req, Net::HTTPResponse res) -> bool + def sspi_auth?: (Net::HTTPResponse res) -> bool + def sspi_auth: (Net::HTTPGenericRequest req) -> untyped + def addr_port: () -> String + def debug: (String msg) -> void + + alias D debug + end + + class HTTPSession = HTTP + + module HTTP::ProxyDelta + def proxy_address: () -> String? + def proxy_port: () -> Integer? + def use_ssl?: () -> bool + def addr_port: () -> String + def conn_address: () -> String? + def conn_port: () -> Integer? + def edit_path: (String path) -> String + end + + class HTTPRequest + METHOD: String + REQUEST_HAS_BODY: bool + RESPONSE_HAS_BODY: bool + end + + class HTTPGenericRequest + @method: String + @request_has_body: bool + @response_has_body: bool + @uri: URI::Generic? + @path: String + @decode_content: bool + @body: String? + @body_stream: untyped + @body_data: Array[untyped] | Hash[untyped, untyped] | nil + + def set_body_internal: (String? str) -> void + def exec: (Net::BufferedIO sock, String ver, String path) -> void + def update_uri: (String address, Integer port, bool ssl) -> void + def pretty_print: (untyped q) -> void + def send_request_with_body: (Net::BufferedIO sock, String ver, String path, String body) -> void + def send_request_with_body_stream: (Net::BufferedIO sock, String ver, String path, untyped body_stream) -> void + def send_request_with_body_data: (Net::BufferedIO sock, String ver, String path, untyped params) -> void + def encode_multipart_form_data: (untyped out, untyped params, untyped opt) -> void + def quote_string: (String str, untyped charset) -> String + def flush_buffer: (untyped out, String? buf, bool chunked_p) -> untyped + def wait_for_continue: (Net::BufferedIO sock, String ver) -> void + def write_header: (Net::BufferedIO sock, String ver, String path) -> Integer + + class Chunker + @sock: Net::BufferedIO + + def initialize: (Net::BufferedIO sock) -> void + def write: (*_ToS buf) -> Integer + def finish: () -> Integer + end + end + + class HTTPResponse + @http_version: String? + @code: String + @message: String? + @body: String? + @read: bool + @uri: URI::Generic? + @decode_content: bool + @body_encoding: Encoding | false | nil + @ignore_eof: bool + @socket: Net::BufferedIO? + @body_exist: bool + + HAS_BODY: bool + EXCEPTION_TYPE: untyped + def self.read_new: (Net::BufferedIO sock) -> Net::HTTPResponse + def self.exception_type: () -> untyped + def self.read_status_line: (Net::BufferedIO sock) -> [String?, String, String?] + def self.response_class: (String code) -> singleton(Net::HTTPResponse) + def self.each_response_header: (Net::BufferedIO sock) { (String, String) -> void } -> void + + def initialize: (String? httpv, String code, String? msg) -> void + def body_encoding=: (Encoding | String | false | nil value) -> (Encoding | false | nil) + def ignore_eof=: (bool value) -> bool + def response: () -> self + def header: () -> self + def read_header: () -> self + def reading_body: (Net::BufferedIO sock, bool reqmethodallowbody) { () -> untyped } -> String? + def detect_encoding: (String str, ?Encoding? encoding) -> Encoding? + def sniff_encoding: (String str, ?Encoding? encoding) -> Encoding? + def check_bom: (String str) -> Encoding? + def scanning_meta: (String str) -> Encoding? + def get_attribute: (untyped ss) -> [String, String]? + def extracting_encodings_from_meta_elements: (String value) -> String? + def inflater: () { (untyped io) -> untyped } -> untyped + def read_body_0: (untyped dest) -> void + def read_chunked: (untyped dest, untyped chunk_data_io) -> void + def stream_check: () -> void + def procdest: (untyped dest, untyped block) -> untyped + + class Inflater + @socket: Net::BufferedIO + @inflate: Zlib::Inflate + + def initialize: (Net::BufferedIO socket) -> void + def finish: () -> void + def bytes_inflated: () -> Integer + def inflate_adapter: (untyped dest) -> Net::ReadAdapter + def read: (Integer clen, untyped dest, ?bool ignore_eof) -> untyped + def read_all: (untyped dest) -> untyped + end + end +end diff --git a/sig/net-http.rbs b/sig/net-http.rbs index b54eb936..14279e8d 100644 --- a/sig/net-http.rbs +++ b/sig/net-http.rbs @@ -14,9 +14,8 @@ module Net # # * [Hypertext Transfer # Protocol](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol). - # * [Technical - # overview](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Techni - # cal_overview). + # * [Technology](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Tec + # hnology). # # ## About the Examples # @@ -56,7 +55,7 @@ module Net # ## Strategies # # * If you will make only a few GET requests, consider using - # [OpenURI](rdoc-ref:OpenURI). + # [OpenURI](https://docs.ruby-lang.org/en/master/OpenURI.html). # * If you will make only a few requests of all kinds, consider using the # various singleton convenience methods in this class. Each of the following # methods automatically starts and finishes a @@ -83,9 +82,8 @@ module Net # * 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) and [WebDAV - # methods](https://en.wikipedia.org/wiki/WebDAV#Implementation): + # 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| # # Session started automatically before block execution. @@ -120,9 +118,9 @@ module Net # scheme, hostname, path, query, and fragment; see [URI # syntax](https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax). # - # A Ruby [URI::Generic](rdoc-ref:URI::Generic) object represents an internet - # URI. It provides, among others, methods `scheme`, `hostname`, `path`, `query`, - # and `fragment`. + # A Ruby [URI::Generic](https://docs.ruby-lang.org/en/master/URI/Generic.html) + # object represents an internet URI. It provides, among others, methods + # `scheme`, `hostname`, `path`, `query`, and `fragment`. # # ### Schemes # @@ -212,8 +210,8 @@ module Net # single request: # # * [HTTP - # methods](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request - # _methods): + # methods](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Method) + # : # # * #get, #request_get: GET. # * #head, #request_head: HEAD. @@ -462,8 +460,8 @@ module Net # has header 'Content-Range'. # # Otherwise decompression (or not) depends on the value of header - # [Content-Encoding](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#co - # ntent-encoding-response-header): + # [Content-Encoding](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Co + # ntent-Encoding_2): # # * 'deflate', 'gzip', or 'x-gzip': # decompresses the body and deletes the header. @@ -475,7 +473,9 @@ module Net # # First, what's elsewhere. Class Net::HTTP: # - # * Inherits from [class Object](rdoc-ref:Object@What-27s+Here). + # * Inherits from [class + # Object](https://docs.ruby-lang.org/en/master/Object.html#class-object-what + # s-here). # # This is a categorized summary of methods and attributes. # @@ -761,7 +761,7 @@ module Net # --> # Like Net::HTTP.get, but writes the returned body to $stdout; returns `nil`. # - def self.get_print: (URI::Generic uri, ?headers header) -> void + def self.get_print: (URI::Generic uri, ?headers? header, ?Integer? port) -> void | (String host, String path, ?Integer port) -> void # + # 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) # => # + # 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 self.put: (URI::HTTP url, String data, ?headers? header) -> Net::HTTPResponse # + # 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 + # + def self.default_configuration: () -> Hash[Symbol, untyped]? + + # + # 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 + # + def self.default_configuration=: (Hash[Symbol, untyped]?) -> Hash[Symbol, untyped]? + + def self.socket_type: () -> singleton(Net::BufferedIO) # # Sets or returns the integer local port used to establish the connection; # initially `nil`. # - attr_accessor local_port: Integer + attr_accessor local_port: Integer? # # Sets whether to determine the proxy from environment variable # 'ENV['http_proxy']'; see {Proxy Using - # [ENV]('http_proxy')}[Net::HTTP@Proxy+Using+-27ENV-5B-27http_proxy-27-5D-27]. + # [ENV]('http_proxy')}[Net::HTTP@Proxy+Using+ENVHTTPProxy]. # attr_writer proxy_from_env: untyped @@ -1141,6 +1209,12 @@ module Net # attr_accessor proxy_pass: String? + # + # Sets whether the proxy uses SSL; see [Proxy + # Server](rdoc-ref:Net::HTTP@Proxy+Server). + # + attr_writer proxy_use_ssl: bool? + # + # Returns the encoding to use for the response body; see + # #response_body_encoding=. + # + def response_body_encoding: () -> (Encoding | String | false | nil) + + # + # Sets the encoding to be used for the response body; returns the encoding. + # + # The given `value` may be: + # + # * An Encoding object. + # * The name of an encoding. + # * An alias for an encoding name. + # + # See [Encoding](https://docs.ruby-lang.org/en/master/Encoding.html). + # + # Examples: + # + # http = Net::HTTP.new(hostname) + # http.response_body_encoding = Encoding::US_ASCII # => # + # http.response_body_encoding = 'US-ASCII' # => "US-ASCII" + # http.response_body_encoding = 'ASCII' # => "ASCII" + # + def response_body_encoding=: (Encoding | String | false | nil) -> (Encoding | String | false | nil) + # # Sets or returns the numeric (Integer or Float) number of seconds to keep the # connection open after a request is sent; initially 2. If a new request is made @@ -1487,7 +1590,7 @@ module Net # 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 self.Proxy: (?interned p_addr, ?Integer? p_port, ?String? p_user, ?String? p_pass) -> untyped + def self.Proxy: (?interned p_addr, ?Integer? p_port, ?String? p_user, ?String? p_pass, ?bool? p_use_ssl) -> untyped # @@ -2052,7 +2155,7 @@ module Net # - new(m, reqbody, resbody, uri_or_path, initheader = nil) # --> # - def initialize: (String m, boolish reqbody, boolish resbody, URI::Generic | String uri_or_path, ?headers initheader) -> void + def initialize: (String m, boolish reqbody, boolish resbody, URI::HTTP | String uri_or_path, ?headers? initheader) -> void # # Returns the string method name for the request: @@ -2077,7 +2180,7 @@ module Net # # => # # Net::HTTP::Get.new('example.com').uri # => nil # - attr_reader uri: URI::Generic + attr_reader uri: URI::Generic? # # Returns `false` if the request's header 'Accept-Encoding' has @@ -2406,12 +2509,22 @@ module Net # * #each_value: Passes each string field value to the block. # module HTTPHeader : BasicObject + # + # The maximum length of HTTP header keys. + # + MAX_KEY_LENGTH: Integer + + # + # The maximum length of HTTP header values. + # + MAX_FIELD_LENGTH: Integer + # # - def initialize_http_header: (Hash[untyped, untyped] initheader) -> void + def initialize_http_header: (Hash[untyped, untyped]? initheader) -> void def size: () -> Integer @@ -2533,9 +2646,8 @@ module Net # res.fetch('Nosuch', 'Foo') # => "Foo" # res.fetch('Nosuch') # Raises KeyError. # - def fetch: (interned key) -> String - | (interned key, untyped) -> untyped - | (interned key) { (String) -> untyped } -> untyped + def fetch: (interned key, *untyped args) -> untyped + | (interned key, *untyped args) { (String) -> untyped } -> untyped # # - def capitalize: (interned name) -> String + def capitalize: (String | Symbol name) -> String public @@ -2770,7 +2882,7 @@ module Net # # Net::HTTPHeader#range= is an alias for Net::HTTPHeader#set_range. # - def set_range: (Range[Integer] | Numeric r, ?Integer? e) -> Range[Integer] + def set_range: (Range[Integer] | Numeric? r, ?Integer? e) -> (Range[Integer] | Numeric | nil) # @@ -3278,12 +3391,9 @@ module Net # # * Request body: optional. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): yes. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): yes. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): yes. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): yes. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): yes. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): yes. # # Related: # @@ -3317,12 +3427,9 @@ module Net # # * Request body: optional. # * Response body: no. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): yes. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): yes. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): yes. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): yes. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): yes. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): yes. # # Related: # @@ -3358,12 +3465,9 @@ module Net # # * Request body: yes. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): no. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): no. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): yes. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): no. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): no. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): yes. # # Related: # @@ -3400,12 +3504,9 @@ module Net # # * Request body: yes. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): no. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): yes. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): no. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): no. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): yes. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): no. # # Related: # @@ -3440,12 +3541,9 @@ module Net # # * Request body: optional. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): no. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): yes. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): no. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): no. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): yes. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): no. # # Related: # @@ -3478,12 +3576,9 @@ module Net # # * Request body: optional. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): yes. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): yes. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): no. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): yes. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): yes. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): no. # # Related: # @@ -3516,12 +3611,9 @@ module Net # # * Request body: no. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): yes. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): yes. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): no. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): yes. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): yes. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): no. # # Related: # @@ -3557,12 +3649,9 @@ module Net # # * Request body: yes. # * Response body: yes. - # * [Safe](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_meth - # ods): no. - # * [Idempotent](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Ide - # mpotent_methods): no. - # * [Cacheable](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Cach - # eable_methods): no. + # * [Safe](https://en.wikipedia.org/wiki/HTTP#Safe_method): no. + # * [Idempotent](https://en.wikipedia.org/wiki/HTTP#Idempotent_method): no. + # * [Cacheable](https://en.wikipedia.org/wiki/HTTP#Cacheable_method): no. # # Related: # @@ -3936,7 +4025,7 @@ module Net # # The HTTP version supported by the server. # - attr_reader http_version: String + attr_reader http_version: String? # # The HTTP result code string. For example, '302'. You can also determine the @@ -3948,7 +4037,7 @@ module Net # # The HTTP result message sent by the server. For example, 'Not Found'. # - attr_reader message: String + attr_reader message: String? # # The HTTP result message sent by the server. For example, 'Not Found'. @@ -3978,7 +4067,7 @@ module Net def error!: () -> untyped - def error_type: () -> (Net::HTTPError | Net::HTTPServerException | Net::HTTPRetriableError | Net::HTTPFatalError) + def error_type: () -> (singleton(Net::HTTPError) | singleton(Net::HTTPClientException) | singleton(Net::HTTPRetriableError) | singleton(Net::HTTPFatalError)) # @@ -4091,7 +4180,7 @@ module Net class HTTPInformation < HTTPResponse HAS_BODY: bool - EXCEPTION_TYPE: Net::HTTPError + EXCEPTION_TYPE: singleton(Net::HTTPError) end # @@ -4109,7 +4198,7 @@ module Net class HTTPSuccess < HTTPResponse HAS_BODY: bool - EXCEPTION_TYPE: Net::HTTPError + EXCEPTION_TYPE: singleton(Net::HTTPError) end # @@ -4127,7 +4216,7 @@ module Net class HTTPRedirection < HTTPResponse HAS_BODY: bool - EXCEPTION_TYPE: Net::HTTPRetriableError + EXCEPTION_TYPE: singleton(Net::HTTPRetriableError) end # @@ -4144,7 +4233,7 @@ module Net class HTTPClientError < HTTPResponse HAS_BODY: bool - EXCEPTION_TYPE: untyped + EXCEPTION_TYPE: singleton(Net::HTTPClientException) end # @@ -4161,7 +4250,7 @@ module Net class HTTPServerError < HTTPResponse HAS_BODY: bool - EXCEPTION_TYPE: Net::HTTPFatalError + EXCEPTION_TYPE: singleton(Net::HTTPFatalError) end # @@ -4483,7 +4572,7 @@ module Net # s). # * [Wikipedia](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes#300). # - HTTPMultipleChoice: HTTPMultipleChoices + class HTTPMultipleChoice = HTTPMultipleChoices # # Response class for `Moved Permanently` responses (status code 301). @@ -5549,6 +5638,20 @@ module Net HTTP::STATUS_CODES: Hash[Integer, String] + class HTTPMovedTemporarily = HTTPFound + + class HTTPRequestTimeOut = HTTPRequestTimeout + + class HTTPRequestEntityTooLarge = HTTPPayloadTooLarge + + class HTTPRequestURITooLong = HTTPURITooLong + + class HTTPRequestURITooLarge = HTTPRequestURITooLong + + class HTTPRequestedRangeNotSatisfiable = HTTPRangeNotSatisfiable + + class HTTPGatewayTimeOut = HTTPGatewayTimeout + # # Net::HTTP exception class. You cannot use Net::HTTPExceptions directly; # instead, you must use its subclasses. @@ -5569,11 +5672,13 @@ module Net include Net::HTTPExceptions end - class HTTPServerException < ProtoServerError + class HTTPClientException < ProtoServerError # We cannot use the name "HTTPServerError", it is the name of the response. include Net::HTTPExceptions end + class HTTPServerException = HTTPClientException + class HTTPFatalError < ProtoFatalError include Net::HTTPExceptions end diff --git a/sig/net-protocol-impl.rbs b/sig/net-protocol-impl.rbs new file mode 100644 index 00000000..5bdb4081 --- /dev/null +++ b/sig/net-protocol-impl.rbs @@ -0,0 +1,94 @@ +module Net + class Protocol + private + + def ssl_socket_connect: (untyped socket, Time::_Timeout? timeout) -> void + 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 + + class OpenTimeout < Timeout::Error + end + + class ReadTimeout < Timeout::Error + attr_reader io: IO? + + def initialize: (?IO? io) -> void + def message: () -> String + end + + class WriteTimeout < Timeout::Error + attr_reader io: IO? + + def initialize: (?IO? io) -> void + def message: () -> String + end + + class BufferedIO + def initialize: ( + untyped io, + ?read_timeout: Time::_Timeout, + ?write_timeout: Time::_Timeout, + ?continue_timeout: Time::_Timeout?, + ?debug_output: IO? + ) -> void + + attr_reader io: untyped + attr_accessor read_timeout: Time::_Timeout + attr_accessor write_timeout: Time::_Timeout + attr_accessor continue_timeout: Time::_Timeout? + attr_accessor debug_output: IO? + + def inspect: () -> String + def eof?: () -> bool + def closed?: () -> bool + def close: () -> untyped + def read: (Integer len, ?untyped dest, ?bool ignore_eof) -> untyped + def read_all: (?untyped dest) -> untyped + def readuntil: (String terminator, ?bool ignore_eof) -> String + def readline: () -> String + def write: (*_ToS strs) -> Integer + def <<: (*_ToS strs) -> Integer + def writeline: (String str) -> Integer + end + + class ReadAdapter + def initialize: (Proc block) -> void + def inspect: () -> String + def <<: (String str) -> untyped + end + +end + +class TCPSocket + def self.new: ( + String remote_host, + Integer remote_port, + ?String? local_host, + ?Integer? local_port, + ?fast_fallback: boolish, + ?open_timeout: Time::_Timeout + ) -> instance +end